# Django imports from django import forms # Local imports from .models import ( Geo, Modulation, ObjItem, Parameter, Polarization, Satellite, Standard, ) from .widgets import CheckboxSelectMultipleWidget class UploadFileForm(forms.Form): file = forms.FileField( label="Выберите файл", widget=forms.FileInput(attrs={"class": "form-file-input"}), ) class LoadExcelData(forms.Form): file = forms.FileField( label="Выберите Excel файл", widget=forms.FileInput(attrs={"class": "form-control", "accept": ".xlsx,.xls"}), ) sat_choice = forms.ModelChoiceField( queryset=Satellite.objects.all(), label="Выберите спутник", widget=forms.Select(attrs={"class": "form-select"}), ) number_input = forms.IntegerField( label="Введите число объектов", min_value=0, widget=forms.NumberInput(attrs={"class": "form-control"}), ) class LoadCsvData(forms.Form): file = forms.FileField( label="Выберите CSV файл", widget=forms.FileInput(attrs={"class": "form-control", "accept": ".csv"}), ) class UploadVchLoad(UploadFileForm): sat_choice = forms.ModelChoiceField( queryset=Satellite.objects.all(), label="Выберите спутник", widget=forms.Select(attrs={"class": "form-select"}), ) class VchLinkForm(forms.Form): sat_choice = forms.ModelChoiceField( queryset=Satellite.objects.all(), label="Выберите спутник", widget=forms.Select(attrs={"class": "form-select"}), ) # ku_range = forms.ChoiceField( # choices=[(9750.0, '9750'), (10750.0, '10750')], # # coerce=lambda x: x == 'True', # widget=forms.Select(attrs={'class': 'form-select'}), # label='Выбор диапазона' # ) value1 = forms.FloatField( label="Разброс по частоте (не используется)", required=False, initial=0.0, widget=forms.NumberInput( attrs={ "class": "form-control", "placeholder": "Не используется - погрешность определяется автоматически", } ), ) value2 = forms.FloatField( label="Разброс по полосе (в %)", widget=forms.NumberInput( attrs={ "class": "form-control", "placeholder": "Введите погрешность полосы в процентах", "step": "0.1", } ), ) class NewEventForm(forms.Form): # sat_choice = forms.ModelChoiceField( # queryset=Satellite.objects.all(), # label="Выберите спутник", # widget=forms.Select(attrs={ # 'class': 'form-select' # }) # ) # pol_choice = forms.ModelChoiceField( # queryset=Polarization.objects.all(), # label="Выберите поляризацию", # widget=forms.Select(attrs={ # 'class': 'form-select' # }) # ) file = forms.FileField( label="Выберите файл", widget=forms.FileInput(attrs={"class": "form-control", "accept": ".xlsx,.xls"}), ) class FillLyngsatDataForm(forms.Form): """Форма для заполнения данных из Lyngsat с поддержкой кеширования""" REGION_CHOICES = [ ("europe", "Европа"), ("asia", "Азия"), ("america", "Америка"), ("atlantic", "Атлантика"), ] satellites = forms.ModelMultipleChoiceField( queryset=Satellite.objects.all().order_by("name"), label="Выберите спутники", widget=forms.SelectMultiple(attrs={"class": "form-select", "size": "10"}), required=True, help_text="Удерживайте Ctrl (Cmd на Mac) для выбора нескольких спутников", ) regions = forms.MultipleChoiceField( choices=REGION_CHOICES, label="Выберите регионы", widget=forms.SelectMultiple(attrs={"class": "form-select", "size": "4"}), required=True, initial=["europe", "asia", "america", "atlantic"], help_text="Удерживайте Ctrl (Cmd на Mac) для выбора нескольких регионов", ) use_cache = forms.BooleanField( label="Использовать кеширование", required=False, initial=True, widget=forms.CheckboxInput(attrs={"class": "form-check-input"}), help_text="Использовать кешированные данные (ускоряет повторные запросы)", ) force_refresh = forms.BooleanField( label="Принудительно обновить данные", required=False, initial=False, widget=forms.CheckboxInput(attrs={"class": "form-check-input"}), help_text="Игнорировать кеш и получить свежие данные с сайта", ) class LinkLyngsatForm(forms.Form): """Форма для привязки источников LyngSat к объектам""" satellites = forms.ModelMultipleChoiceField( queryset=Satellite.objects.all().order_by("name"), label="Выберите спутники", widget=forms.SelectMultiple(attrs={"class": "form-select", "size": "10"}), required=False, help_text="Оставьте пустым для обработки всех спутников", ) frequency_tolerance = forms.FloatField( label="Допуск по частоте (МГц)", initial=0.5, min_value=0, widget=forms.NumberInput(attrs={"class": "form-control", "step": "0.1"}), help_text="Допустимое отклонение частоты при сравнении", ) class ParameterForm(forms.ModelForm): """ Форма для создания и редактирования параметров ВЧ загрузки. Работает с одним экземпляром Parameter, связанным с ObjItem через OneToOne связь. """ class Meta: model = Parameter fields = [ "id_satellite", "frequency", "freq_range", "polarization", "bod_velocity", "modulation", "snr", "standard", ] widgets = { "id_satellite": forms.Select( attrs={"class": "form-select", "required": True} ), "frequency": forms.NumberInput( attrs={ "class": "form-control", "step": "0.000001", "min": "0", "max": "50000", "placeholder": "Введите частоту в МГц", } ), "freq_range": forms.NumberInput( attrs={ "class": "form-control", "step": "0.000001", "min": "0", "max": "1000", "placeholder": "Введите полосу частот в МГц", } ), "bod_velocity": forms.NumberInput( attrs={ "class": "form-control", "step": "0.001", "min": "0", "placeholder": "Введите символьную скорость в БОД", } ), "snr": forms.NumberInput( attrs={ "class": "form-control", "step": "0.001", "min": "-50", "max": "100", "placeholder": "Введите ОСШ в дБ", } ), "polarization": forms.Select(attrs={"class": "form-select"}), "modulation": forms.Select(attrs={"class": "form-select"}), "standard": forms.Select(attrs={"class": "form-select"}), } labels = { "id_satellite": "Спутник", "frequency": "Частота (МГц)", "freq_range": "Полоса частот (МГц)", "polarization": "Поляризация", "bod_velocity": "Символьная скорость (БОД)", "modulation": "Модуляция", "snr": "ОСШ (дБ)", "standard": "Стандарт", } help_texts = { "frequency": "Частота в диапазоне от 0 до 50000 МГц", "freq_range": "Полоса частот в диапазоне от 0 до 1000 МГц", "bod_velocity": "Символьная скорость должна быть положительной", "snr": "Отношение сигнал/шум в диапазоне от -50 до 100 дБ", } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Динамически загружаем choices для select полей self.fields["id_satellite"].queryset = Satellite.objects.all().order_by("name") self.fields["polarization"].queryset = Polarization.objects.all().order_by( "name" ) self.fields["modulation"].queryset = Modulation.objects.all().order_by("name") self.fields["standard"].queryset = Standard.objects.all().order_by("name") # Делаем спутник обязательным полем self.fields["id_satellite"].required = True def clean(self): """ Дополнительная валидация формы. Проверяет соотношение между частотой, полосой частот и символьной скоростью. """ cleaned_data = super().clean() frequency = cleaned_data.get("frequency") freq_range = cleaned_data.get("freq_range") bod_velocity = cleaned_data.get("bod_velocity") # Проверка что частота больше полосы частот if frequency and freq_range: if freq_range > frequency: self.add_error( "freq_range", "Полоса частот не может быть больше частоты" ) # Проверка что символьная скорость соответствует полосе частот if bod_velocity and freq_range: if bod_velocity > freq_range * 1000000: # Конвертация МГц в Гц self.add_error( "bod_velocity", "Символьная скорость не может превышать полосу частот", ) return cleaned_data class GeoForm(forms.ModelForm): class Meta: model = Geo fields = ["location", "comment", "is_average", "mirrors"] widgets = { "location": forms.TextInput(attrs={"class": "form-control"}), "comment": forms.TextInput(attrs={"class": "form-control"}), "is_average": forms.CheckboxInput(attrs={"class": "form-check-input"}), "mirrors": CheckboxSelectMultipleWidget( attrs={ 'id': 'id_geo-mirrors', 'placeholder': 'Выберите спутники...', } ), } labels = { "location": "Местоположение", "comment": "Комментарий", "is_average": "Усреднённое", "mirrors": "Спутники-зеркала, использованные для приёма", } help_texts = { "mirrors": "Выберите спутники из списка", } class ObjItemForm(forms.ModelForm): """ Форма для создания и редактирования объектов (источников сигнала). Работает с моделью ObjItem. Параметры ВЧ загрузки обрабатываются отдельно через ParameterForm с использованием OneToOne связи. """ class Meta: model = ObjItem fields = ["name"] widgets = { "name": forms.TextInput( attrs={ "class": "form-control", "placeholder": "Введите название объекта", "maxlength": "100", } ), } labels = { "name": "Название объекта", } help_texts = { "name": "Уникальное название объекта/источника сигнала", } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Делаем поле name необязательным, так как оно может быть пустым self.fields["name"].required = False def clean_name(self): """ Валидация поля name. Проверяет что название не состоит только из пробелов. """ name = self.cleaned_data.get("name") if name: # Удаляем лишние пробелы name = name.strip() # Проверяем что после удаления пробелов что-то осталось if not name: raise forms.ValidationError( "Название не может состоять только из пробелов" ) return name