# Django imports from django import forms # Local imports from .models import Geo, Modulation, ObjItem, Parameter, Polarization, Satellite, Standard from .widgets import TagSelectWidget 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': TagSelectWidget(attrs={'id': 'id_geo-mirrors'}), } 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