Сделал деплой

This commit is contained in:
2025-11-23 22:55:32 +03:00
parent 0d239ef1de
commit 9a9900cfa6
15 changed files with 4191 additions and 4217 deletions

View File

@@ -1,285 +1,285 @@
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
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