Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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.
|
694 |
'P': 0, 'K': 0, 'Mg': 0, 'Ca': 0, 'S': 0,
|
695 |
-
'NO3': 0, 'NH4': 0 #
|
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.
|
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 |
-
"
|
725 |
-
"K": float(fertilizers_data["
|
726 |
},
|
727 |
"Аммоний азотнокислый": {
|
728 |
"NO3": float(fertilizers_data["Аммоний азотнокислый"]["N (NO3-)"]),
|
729 |
"NH4": float(fertilizers_data["Аммоний азотнокислый"]["N (NH4+)"])
|
730 |
},
|
731 |
-
"
|
732 |
-
"
|
733 |
-
"
|
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. Вносим
|
749 |
self._apply_fertilizer("Сульфат магния", "Mg")
|
750 |
-
|
751 |
-
# 2. Вносим монофосфат калия (P + K)
|
752 |
self._apply_fertilizer("Монофосфат калия", "P")
|
753 |
|
754 |
-
#
|
755 |
self._apply_fertilizer("Аммоний азотнокислый", "NH4")
|
756 |
|
757 |
-
#
|
758 |
-
|
759 |
-
if
|
760 |
self._apply_fertilizer("Кальциевая селитра", "Ca")
|
761 |
|
762 |
-
#
|
763 |
-
k_deficit = self.
|
764 |
if k_deficit > 0.1:
|
765 |
-
|
766 |
-
|
767 |
-
self._apply_fertilizer("Калий азотнокислый", "K", k_deficit)
|
768 |
else:
|
769 |
-
self._apply_fertilizer("Калий сернокислый", "K"
|
770 |
|
771 |
-
#
|
772 |
-
s_deficit = self.
|
773 |
if s_deficit > 0.1:
|
774 |
-
self._apply_fertilizer("Калий сернокислый", "S"
|
775 |
|
776 |
return True
|
777 |
except Exception as e:
|
778 |
-
print(f"
|
779 |
return False
|
780 |
|
781 |
def _apply_fertilizer(self, name, main_element, required_ppm=None):
|
782 |
-
"""
|
|
|
|
|
|
|
783 |
if required_ppm is None:
|
784 |
-
required_ppm = self.
|
785 |
|
786 |
-
if required_ppm <= 0
|
787 |
return
|
788 |
|
789 |
content = self.fertilizers[name].get(main_element, 0)
|
790 |
if content <= 0:
|
791 |
return
|
792 |
|
793 |
-
#
|
794 |
if 'NO3' in self.fertilizers[name]:
|
795 |
-
|
796 |
-
if
|
797 |
-
|
|
|
|
|
|
|
|
|
|
|
798 |
|
799 |
-
|
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.
|
811 |
-
self.results[name]['adds'][element]
|
812 |
-
|
813 |
# Расчёт EC
|
814 |
-
|
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 *
|
821 |
-
for
|
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 |
-
|
837 |
-
|
838 |
-
|
|
|
|
|
|
|
839 |
],
|
840 |
'ec': round(self.total_ec, 2),
|
841 |
'deficits': {
|
842 |
-
'
|
843 |
-
max(self.
|
844 |
-
|
|
|
|
|
|
|
|
|
845 |
}
|
846 |
}
|
847 |
|
848 |
-
@app.route('/
|
849 |
def handle_calculation():
|
850 |
try:
|
851 |
data = request.json
|
852 |
|
853 |
-
# Валидация
|
854 |
-
|
855 |
-
if not all(
|
856 |
-
return jsonify({'status': 'error', 'message': 'Missing required
|
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',
|