286 lines
13 KiB
Python
286 lines
13 KiB
Python
from django.contrib.auth.mixins import LoginRequiredMixin
|
||
from django.core.paginator import Paginator
|
||
from django.db.models import Q
|
||
from django.views.generic import ListView
|
||
|
||
from .models import LyngSat
|
||
from mainapp.models import Satellite, Polarization, Modulation, Standard
|
||
from mainapp.utils import parse_pagination_params
|
||
|
||
|
||
class LyngSatListView(LoginRequiredMixin, ListView):
|
||
"""
|
||
Представление для отображения списка источников LyngSat с фильтрацией и пагинацией.
|
||
"""
|
||
model = LyngSat
|
||
template_name = 'lyngsatapp/lyngsat_list.html'
|
||
context_object_name = 'lyngsat_items'
|
||
paginate_by = 50
|
||
|
||
def get_queryset(self):
|
||
"""
|
||
Возвращает отфильтрованный и отсортированный queryset.
|
||
"""
|
||
queryset = LyngSat.objects.select_related(
|
||
'id_satellite',
|
||
'polarization',
|
||
'modulation',
|
||
'standard'
|
||
).all()
|
||
|
||
# Поиск по ID
|
||
search_query = self.request.GET.get('search', '').strip()
|
||
if search_query:
|
||
try:
|
||
search_id = int(search_query)
|
||
queryset = queryset.filter(id=search_id)
|
||
except ValueError:
|
||
queryset = queryset.none()
|
||
|
||
# Фильтр по спутнику
|
||
satellite_ids = self.request.GET.getlist('satellite_id')
|
||
if satellite_ids:
|
||
queryset = queryset.filter(id_satellite_id__in=satellite_ids)
|
||
|
||
# Фильтр по поляризации
|
||
polarization_ids = self.request.GET.getlist('polarization_id')
|
||
if polarization_ids:
|
||
queryset = queryset.filter(polarization_id__in=polarization_ids)
|
||
|
||
# Фильтр по модуляции
|
||
modulation_ids = self.request.GET.getlist('modulation_id')
|
||
if modulation_ids:
|
||
queryset = queryset.filter(modulation_id__in=modulation_ids)
|
||
|
||
# Фильтр по стандарту
|
||
standard_ids = self.request.GET.getlist('standard_id')
|
||
if standard_ids:
|
||
queryset = queryset.filter(standard_id__in=standard_ids)
|
||
|
||
# Фильтр по частоте
|
||
freq_min = self.request.GET.get('freq_min', '').strip()
|
||
freq_max = self.request.GET.get('freq_max', '').strip()
|
||
if freq_min:
|
||
try:
|
||
queryset = queryset.filter(frequency__gte=float(freq_min))
|
||
except ValueError:
|
||
pass
|
||
if freq_max:
|
||
try:
|
||
queryset = queryset.filter(frequency__lte=float(freq_max))
|
||
except ValueError:
|
||
pass
|
||
|
||
# Фильтр по символьной скорости
|
||
sym_min = self.request.GET.get('sym_min', '').strip()
|
||
sym_max = self.request.GET.get('sym_max', '').strip()
|
||
if sym_min:
|
||
try:
|
||
queryset = queryset.filter(sym_velocity__gte=float(sym_min))
|
||
except ValueError:
|
||
pass
|
||
if sym_max:
|
||
try:
|
||
queryset = queryset.filter(sym_velocity__lte=float(sym_max))
|
||
except ValueError:
|
||
pass
|
||
|
||
# Фильтр по дате обновления
|
||
date_from = self.request.GET.get('date_from', '').strip()
|
||
date_to = self.request.GET.get('date_to', '').strip()
|
||
if date_from:
|
||
queryset = queryset.filter(last_update__gte=date_from)
|
||
if date_to:
|
||
queryset = queryset.filter(last_update__lte=date_to)
|
||
|
||
# Сортировка
|
||
sort = self.request.GET.get('sort', '-id')
|
||
valid_sort_fields = ['id', '-id', 'frequency', '-frequency', 'sym_velocity', '-sym_velocity', 'last_update', '-last_update']
|
||
if sort in valid_sort_fields:
|
||
queryset = queryset.order_by(sort)
|
||
else:
|
||
queryset = queryset.order_by('-id')
|
||
|
||
return queryset
|
||
|
||
def get_context_data(self, **kwargs):
|
||
"""
|
||
Добавляет дополнительный контекст для шаблона.
|
||
"""
|
||
context = super().get_context_data(**kwargs)
|
||
|
||
# Параметры пагинации
|
||
page_number, items_per_page = parse_pagination_params(self.request, default_per_page=50)
|
||
context['items_per_page'] = items_per_page
|
||
context['available_items_per_page'] = [25, 50, 100, 200, 500]
|
||
|
||
# Пагинация
|
||
paginator = Paginator(self.get_queryset(), items_per_page)
|
||
page_obj = paginator.get_page(page_number)
|
||
context['page_obj'] = page_obj
|
||
context['lyngsat_items'] = page_obj.object_list
|
||
|
||
# Параметры поиска и фильтрации
|
||
context['search_query'] = self.request.GET.get('search', '')
|
||
context['sort'] = self.request.GET.get('sort', '-id')
|
||
|
||
# Данные для фильтров - только спутники с существующими записями LyngSat
|
||
satellites = Satellite.objects.filter(
|
||
lyngsat__isnull=False
|
||
).distinct().order_by('name')
|
||
polarizations = Polarization.objects.all().order_by('name')
|
||
modulations = Modulation.objects.all().order_by('name')
|
||
standards = Standard.objects.all().order_by('name')
|
||
|
||
# Выбранные фильтры
|
||
selected_satellites = [int(x) for x in self.request.GET.getlist('satellite_id') if x.isdigit()]
|
||
selected_polarizations = [int(x) for x in self.request.GET.getlist('polarization_id') if x.isdigit()]
|
||
selected_modulations = [int(x) for x in self.request.GET.getlist('modulation_id') if x.isdigit()]
|
||
selected_standards = [int(x) for x in self.request.GET.getlist('standard_id') if x.isdigit()]
|
||
|
||
# Параметры фильтров
|
||
freq_min = self.request.GET.get('freq_min', '')
|
||
freq_max = self.request.GET.get('freq_max', '')
|
||
sym_min = self.request.GET.get('sym_min', '')
|
||
sym_max = self.request.GET.get('sym_max', '')
|
||
date_from = self.request.GET.get('date_from', '')
|
||
date_to = self.request.GET.get('date_to', '')
|
||
|
||
# Action buttons HTML for toolbar component
|
||
from django.urls import reverse
|
||
action_buttons_html = f'''
|
||
<a href="{reverse('mainapp:fill_lyngsat_data')}" class="btn btn-secondary btn-sm" title="Заполнить данные Lyngsat">
|
||
<i class="bi bi-cloud-download"></i> Добавить данные
|
||
</a>
|
||
<a href="{reverse('mainapp:link_lyngsat')}" class="btn btn-primary btn-sm" title="Привязать источники LyngSat">
|
||
<i class="bi bi-link-45deg"></i> Привязать
|
||
</a>
|
||
<a href="{reverse('mainapp:unlink_all_lyngsat')}" class="btn btn-warning btn-sm" title="Отвязать все источники LyngSat">
|
||
<i class="bi bi-x-circle"></i> Отвязать
|
||
</a>
|
||
'''
|
||
context['action_buttons_html'] = action_buttons_html
|
||
|
||
# Build filter HTML list for filter_panel component
|
||
filter_html_list = []
|
||
|
||
# Satellite filter
|
||
satellite_options = ''.join([
|
||
f'<option value="{sat.id}" {"selected" if sat.id in selected_satellites else ""}>{sat.name}</option>'
|
||
for sat in satellites
|
||
])
|
||
filter_html_list.append(f'''
|
||
<div class="mb-2">
|
||
<label class="form-label">Спутник:</label>
|
||
<div class="d-flex justify-content-between mb-1">
|
||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||
onclick="selectAllOptions('satellite_id', true)">Выбрать</button>
|
||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||
onclick="selectAllOptions('satellite_id', false)">Снять</button>
|
||
</div>
|
||
<select name="satellite_id" class="form-select form-select-sm mb-2" multiple size="6">
|
||
{satellite_options}
|
||
</select>
|
||
</div>
|
||
''')
|
||
|
||
# Polarization filter
|
||
polarization_options = ''.join([
|
||
f'<option value="{pol.id}" {"selected" if pol.id in selected_polarizations else ""}>{pol.name}</option>'
|
||
for pol in polarizations
|
||
])
|
||
filter_html_list.append(f'''
|
||
<div class="mb-2">
|
||
<label class="form-label">Поляризация:</label>
|
||
<div class="d-flex justify-content-between mb-1">
|
||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||
onclick="selectAllOptions('polarization_id', true)">Выбрать</button>
|
||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||
onclick="selectAllOptions('polarization_id', false)">Снять</button>
|
||
</div>
|
||
<select name="polarization_id" class="form-select form-select-sm mb-2" multiple size="4">
|
||
{polarization_options}
|
||
</select>
|
||
</div>
|
||
''')
|
||
|
||
# Modulation filter
|
||
modulation_options = ''.join([
|
||
f'<option value="{mod.id}" {"selected" if mod.id in selected_modulations else ""}>{mod.name}</option>'
|
||
for mod in modulations
|
||
])
|
||
filter_html_list.append(f'''
|
||
<div class="mb-2">
|
||
<label class="form-label">Модуляция:</label>
|
||
<div class="d-flex justify-content-between mb-1">
|
||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||
onclick="selectAllOptions('modulation_id', true)">Выбрать</button>
|
||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||
onclick="selectAllOptions('modulation_id', false)">Снять</button>
|
||
</div>
|
||
<select name="modulation_id" class="form-select form-select-sm mb-2" multiple size="4">
|
||
{modulation_options}
|
||
</select>
|
||
</div>
|
||
''')
|
||
|
||
# Standard filter
|
||
standard_options = ''.join([
|
||
f'<option value="{std.id}" {"selected" if std.id in selected_standards else ""}>{std.name}</option>'
|
||
for std in standards
|
||
])
|
||
filter_html_list.append(f'''
|
||
<div class="mb-2">
|
||
<label class="form-label">Стандарт:</label>
|
||
<div class="d-flex justify-content-between mb-1">
|
||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||
onclick="selectAllOptions('standard_id', true)">Выбрать</button>
|
||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||
onclick="selectAllOptions('standard_id', false)">Снять</button>
|
||
</div>
|
||
<select name="standard_id" class="form-select form-select-sm mb-2" multiple size="4">
|
||
{standard_options}
|
||
</select>
|
||
</div>
|
||
''')
|
||
|
||
# Frequency filter
|
||
filter_html_list.append(f'''
|
||
<div class="mb-2">
|
||
<label class="form-label">Частота, МГц:</label>
|
||
<input type="number" step="0.001" name="freq_min" class="form-control form-control-sm mb-1"
|
||
placeholder="От" value="{freq_min}">
|
||
<input type="number" step="0.001" name="freq_max" class="form-control form-control-sm"
|
||
placeholder="До" value="{freq_max}">
|
||
</div>
|
||
''')
|
||
|
||
# Symbol rate filter
|
||
filter_html_list.append(f'''
|
||
<div class="mb-2">
|
||
<label class="form-label">Символьная скорость, БОД:</label>
|
||
<input type="number" step="0.001" name="sym_min" class="form-control form-control-sm mb-1"
|
||
placeholder="От" value="{sym_min}">
|
||
<input type="number" step="0.001" name="sym_max" class="form-control form-control-sm"
|
||
placeholder="До" value="{sym_max}">
|
||
</div>
|
||
''')
|
||
|
||
# Date filter
|
||
filter_html_list.append(f'''
|
||
<div class="mb-2">
|
||
<label class="form-label">Дата обновления:</label>
|
||
<input type="date" name="date_from" id="date_from" class="form-control form-control-sm mb-1"
|
||
placeholder="От" value="{date_from}">
|
||
<input type="date" name="date_to" id="date_to" class="form-control form-control-sm"
|
||
placeholder="До" value="{date_to}">
|
||
</div>
|
||
''')
|
||
|
||
context['filter_html_list'] = filter_html_list
|
||
|
||
# Enable full width layout
|
||
context['full_width_page'] = True
|
||
|
||
return context
|