Доделал страницу с Кубсатами

This commit is contained in:
2025-11-20 10:50:27 +03:00
parent 66e1929978
commit 1d1c42a8e7
6 changed files with 171 additions and 139 deletions

View File

@@ -54,8 +54,23 @@
- Количество точек автоматически пересчитывается при удалении строк
- Таблица имеет фиксированную высоту с прокруткой и sticky заголовок
### Выделение по дате
Строки, у которых дата ГЛ попадает в выбранный диапазон дат, **выделяются зеленым цветом**. При этом все объекты остаются в таблице, независимо от фильтра по дате.
### Двухэтапная фильтрация
Фильтрация происходит в два этапа:
**Этап 1: Фильтрация по дате ГЛ**
- Если задан диапазон дат (от/до), отображаются только те точки, у которых дата ГЛ попадает в этот диапазон
- Точки без даты ГЛ исключаются, если фильтр по дате задан
- Если фильтр не задан, показываются все точки
**Этап 2: Фильтрация по количеству точек**
- Применяется к уже отфильтрованным по дате точкам
- Варианты:
- "Все" - показываются источники с любым количеством точек
- "1" - только источники с ровно 1 точкой (после фильтрации по дате)
- "2 и более" - только источники с 2 и более точками (после фильтрации по дате)
**Важно**: Фильтр по количеству точек учитывает только те точки, которые прошли фильтрацию по дате!
### Управление данными в таблице
@@ -63,10 +78,7 @@
- **Кнопка с иконкой корзины** (для каждой точки) - удаляет конкретную точку (ObjItem) из таблицы
- **Кнопка с иконкой заполненной корзины** (для первой точки источника) - удаляет все точки источника (Source) из таблицы
**Кнопка "Оставить только подходящие по дате":**
- Удаляет из таблицы все точки, которые НЕ подходят по заданному диапазону дат
- Оставляет только зеленые (выделенные) строки
- Запрашивает подтверждение перед удалением
**Важно**: Все удаления происходят только из таблицы, **БЕЗ удаления из базы данных**. Это позволяет пользователю исключить ненужные записи перед экспортом.
@@ -83,7 +95,7 @@
8. **Обратный канал, МГц** - частота источника
9. **Перенос** - из объекта Transponder
10. **Получено координат, раз** - количество точек (ObjItem), оставшихся в таблице для данного источника
11. **Дата** - пока заполняется как "-"
11. **Период получения координат** - диапазон дат ГЛ в формате "5.11.2025-15.11.2025" (от самой ранней до самой поздней даты среди точек источника). Если все точки имеют одну дату, показывается только одна дата.
12. **Зеркала** - все имена зеркал через перенос строки (из оставшихся точек)
13. **СКО, км** - не заполняется
14. **Примечание** - не заполняется
@@ -123,14 +135,13 @@ queryset = Source.objects.select_related('info').prefetch_related(
1. Откройте страницу "Кубсат" из навигационного меню
2. Выберите нужные фильтры (спутники, поляризация, частота и т.д.)
3. Опционально укажите диапазон дат для выделения подходящих точек
4. Нажмите "Применить фильтры"
5. В таблице отобразятся все точки (ObjItem) сгруппированные по источникам (Source)
6. Точки, подходящие по дате, будут выделены зеленым цветом
7. Опционально нажмите "Оставить только подходящие по дате" для быстрого удаления неподходящих точек
8. При необходимости удалите отдельные точки или целые объекты кнопками в колонке "Действия"
9. Нажмите "Экспорт в Excel" для скачивания файла с оставшимися данными
10. Форма не сбрасывается после экспорта - можно продолжить работу
3. Опционально укажите диапазон дат для фильтрации точек по дате ГЛ (Этап 1)
4. Опционально выберите количество точек (1 или 2+) - применяется к отфильтрованным по дате точкам (Этап 2)
5. Нажмите "Применить фильтры"
6. В таблице отобразятся точки (ObjItem) сгруппированные по источникам (Source)
7. При необходимости удалите отдельные точки или целые объекты кнопками в колонке "Действия"
8. Нажмите "Экспорт в Excel" для скачивания файла с оставшимися данными
9. Форма не сбрасывается после экспорта - можно продолжить работу
## Примечания
@@ -138,5 +149,6 @@ queryset = Source.objects.select_related('info').prefetch_related(
- Удаление точек/объектов из таблицы не влияет на базу данных
- Экспортируются только оставшиеся в таблице точки
- Координаты в Excel рассчитываются как инкрементальное среднее из оставшихся точек
- Фильтр по дате не скрывает объекты, а только выделяет их цветом
- Фильтр по дате скрывает неподходящие точки (не показывает их в таблице)
- Каждая строка таблицы = одна точка (ObjItem), строки группируются по источникам (Source)
- Количество точек в колонке "Кол-во точек" автоматически пересчитывается при удалении строк

View File

@@ -463,7 +463,7 @@ class SourceForm(forms.ModelForm):
class Meta:
model = Source
fields = ['info'] # Добавляем поле info
fields = ['info']
widgets = {
'info': forms.Select(attrs={
'class': 'form-select',
@@ -555,18 +555,18 @@ class KubsatFilterForm(forms.Form):
widget=forms.SelectMultiple(attrs={'class': 'form-select', 'size': '5'})
)
band = forms.ModelChoiceField(
band = forms.ModelMultipleChoiceField(
queryset=None,
label='Полоса спутника',
label='Диапазоны работы спутника',
required=False,
widget=forms.Select(attrs={'class': 'form-select'})
widget=forms.SelectMultiple(attrs={'class': 'form-select', 'size': '4'})
)
polarization = forms.ModelMultipleChoiceField(
queryset=Polarization.objects.all().order_by('name'),
label='Поляризация',
required=False,
widget=forms.SelectMultiple(attrs={'class': 'form-select', 'size': '3'})
widget=forms.SelectMultiple(attrs={'class': 'form-select', 'size': '4'})
)
frequency_min = forms.FloatField(
@@ -597,7 +597,7 @@ class KubsatFilterForm(forms.Form):
queryset=Modulation.objects.all().order_by('name'),
label='Модуляция',
required=False,
widget=forms.SelectMultiple(attrs={'class': 'form-select', 'size': '3'})
widget=forms.SelectMultiple(attrs={'class': 'form-select', 'size': '4'})
)
object_type = forms.ModelMultipleChoiceField(
@@ -624,22 +624,22 @@ class KubsatFilterForm(forms.Form):
# Фиктивные фильтры
has_plans = forms.ChoiceField(
choices=[('', 'Все'), ('yes', 'Да'), ('no', 'Нет')],
label='Планы на',
choices=[('', 'Неважно'), ('yes', 'Да'), ('no', 'Нет')],
label='Планы на Кубсат',
required=False,
widget=forms.RadioSelect()
)
success_1 = forms.ChoiceField(
choices=[('', 'Все'), ('yes', 'Да'), ('no', 'Нет')],
label='Успех 1',
choices=[('', 'Неважно'), ('yes', 'Да'), ('no', 'Нет')],
label='ГСО успешно?',
required=False,
widget=forms.RadioSelect()
)
success_2 = forms.ChoiceField(
choices=[('', 'Все'), ('yes', 'Да'), ('no', 'Нет')],
label='Успех 2',
choices=[('', 'Неважно'), ('yes', 'Да'), ('no', 'Нет')],
label='Кубсат успешно?',
required=False,
widget=forms.RadioSelect()
)

View File

@@ -37,11 +37,11 @@
<li class="nav-item">
<a class="nav-link" href="{% url 'mainapp:kubsat' %}">Кубсат</a>
</li>
<li class="nav-item">
<!-- <li class="nav-item">
<a class="nav-link" href="{% url 'mapsapp:3dmap' %}">3D карта</a>
</li>
</li> -->
<li class="nav-item">
<a class="nav-link" href="{% url 'mapsapp:2dmap' %}">2D карта</a>
<a class="nav-link" href="{% url 'mapsapp:2dmap' %}">Карта</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'admin:index' %}">Админ панель</a>

View File

@@ -23,6 +23,12 @@
<!-- Спутники -->
<div class="col-md-3 mb-3">
<label for="{{ form.satellites.id_for_label }}" class="form-label">{{ form.satellites.label }}</label>
<div class="d-flex justify-content-between mb-1">
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="selectAllOptions('satellites', true)">Выбрать</button>
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="selectAllOptions('satellites', false)">Снять</button>
</div>
{{ form.satellites }}
<small class="form-text text-muted">Удерживайте Ctrl для выбора нескольких</small>
</div>
@@ -30,19 +36,40 @@
<!-- Полоса спутника -->
<div class="col-md-3 mb-3">
<label for="{{ form.band.id_for_label }}" class="form-label">{{ form.band.label }}</label>
<div class="d-flex justify-content-between mb-1">
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="selectAllOptions('band', true)">Выбрать</button>
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="selectAllOptions('band', false)">Снять</button>
</div>
{{ form.band }}
<small class="form-text text-muted">Удерживайте Ctrl для выбора нескольких</small>
</div>
<!-- Поляризация -->
<div class="col-md-3 mb-3">
<label for="{{ form.polarization.id_for_label }}" class="form-label">{{ form.polarization.label }}</label>
<div class="d-flex justify-content-between mb-1">
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="selectAllOptions('polarization', true)">Выбрать</button>
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="selectAllOptions('polarization', false)">Снять</button>
</div>
{{ form.polarization }}
<small class="form-text text-muted">Удерживайте Ctrl для выбора нескольких</small>
</div>
<!-- Модуляция -->
<div class="col-md-3 mb-3">
<label for="{{ form.modulation.id_for_label }}" class="form-label">{{ form.modulation.label }}</label>
<div class="d-flex justify-content-between mb-1">
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="selectAllOptions('modulation', true)">Выбрать</button>
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="selectAllOptions('modulation', false)">Снять</button>
</div>
{{ form.modulation }}
<small class="form-text text-muted">Удерживайте Ctrl для выбора нескольких</small>
</div>
</div>
@@ -70,12 +97,19 @@
<!-- Тип объекта -->
<div class="col-md-3 mb-3">
<label for="{{ form.object_type.id_for_label }}" class="form-label">{{ form.object_type.label }}</label>
<div class="d-flex justify-content-between mb-1">
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="selectAllOptions('object_type', true)">Выбрать</button>
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="selectAllOptions('object_type', false)">Снять</button>
</div>
{{ form.object_type }}
<small class="form-text text-muted">Удерживайте Ctrl для выбора нескольких</small>
</div>
<!-- Принадлежность объекта (заглушка) -->
<div class="col-md-3 mb-3">
<label for="{{ form.object_ownership.id_for_label }}" class="form-label">{{ form.object_ownership.label }} <span class="badge bg-secondary">Заглушка</span></label>
<label for="{{ form.object_ownership.id_for_label }}" class="form-label">{{ form.object_ownership.label }} (не работает)</label>
{{ form.object_ownership }}
</div>
</div>
@@ -98,7 +132,7 @@
<!-- Планы на (фиктивный) -->
<div class="col-md-3 mb-3">
<label class="form-label">{{ form.has_plans.label }} <span class="badge bg-secondary">Фиктивный</span></label>
<label class="form-label">{{ form.has_plans.label }} (не работает)</label>
<div>
{% for radio in form.has_plans %}
<div class="form-check">
@@ -113,7 +147,7 @@
<!-- Успех 1 (фиктивный) -->
<div class="col-md-3 mb-3">
<label class="form-label">{{ form.success_1.label }} <span class="badge bg-secondary">Фиктивный</span></label>
<label class="form-label">{{ form.success_1.label }} (не рабльает)</label>
<div>
{% for radio in form.success_1 %}
<div class="form-check">
@@ -128,7 +162,7 @@
<!-- Успех 2 (фиктивный) -->
<div class="col-md-3 mb-3">
<label class="form-label">{{ form.success_2.label }} <span class="badge bg-secondary">Фиктивный</span></label>
<label class="form-label">{{ form.success_2.label }} (не работает)</label>
<div>
{% for radio in form.success_2 %}
<div class="form-check">
@@ -145,7 +179,7 @@
<div class="row">
<!-- Диапазон дат (фиктивный) -->
<div class="col-md-6 mb-3">
<label class="form-label">Диапазон дат <span class="badge bg-secondary">Фиктивный</span></label>
<label class="form-label">Диапазон дат ГЛ:</label>
<div class="input-group">
{{ form.date_from }}
<span class="input-group-text"></span>
@@ -164,7 +198,7 @@
</div>
</form>
<!-- Кнопки экспорта и фильтрации -->
<!-- Кнопка экспорта и статистика -->
{% if sources_with_date_info %}
<div class="row mb-3">
<div class="col-12">
@@ -174,10 +208,10 @@
<button type="button" class="btn btn-success" onclick="exportToExcel()">
<i class="bi bi-file-earmark-excel"></i> Экспорт в Excel
</button>
<button type="button" class="btn btn-warning" onclick="filterByDateMatch()">
<i class="bi bi-funnel"></i> Оставить только подходящие по дате
</button>
<span class="ms-auto text-muted"></span>
<span class="text-muted" id="statsCounter">
Найдено объектов: {{ sources_with_date_info|length }},
точек: {% for source_data in sources_with_date_info %}{{ source_data.objitems_data|length }}{% if not forloop.last %}+{% endif %}{% endfor %}
</span>
</div>
</div>
</div>
@@ -195,7 +229,7 @@
<table class="table table-striped table-hover table-sm table-bordered" style="font-size: 0.85rem;" id="resultsTable">
<thead class="table-dark sticky-top">
<tr>
<th style="min-width: 80px;">ID Source</th>
<th style="min-width: 80px;">ID объекта</th>
<th style="min-width: 120px;">Тип объекта</th>
<th class="text-center" style="min-width: 100px;">Кол-во точек</th>
<th style="min-width: 120px;">Имя точки</th>
@@ -214,7 +248,6 @@
{% for objitem_data in source_data.objitems_data %}
<tr data-source-id="{{ source_data.source.id }}"
data-objitem-id="{{ objitem_data.objitem.id }}"
class="{% if objitem_data.matches_date %}table-success{% endif %}"
data-matches-date="{{ objitem_data.matches_date|yesno:'true,false' }}"
data-is-first-in-source="{% if forloop.first %}true{% else %}false{% endif %}">
@@ -251,7 +284,7 @@
<!-- Частота -->
<td>
{% if objitem_data.objitem.parameter_obj %}
{{ objitem_data.objitem.parameter_obj.frequency|default:"-" }}
{{ objitem_data.objitem.parameter_obj.frequency|default:"-"|floatformat:3 }}
{% else %}
-
{% endif %}
@@ -260,7 +293,7 @@
<!-- Полоса -->
<td>
{% if objitem_data.objitem.parameter_obj %}
{{ objitem_data.objitem.parameter_obj.freq_range|default:"-" }}
{{ objitem_data.objitem.parameter_obj.freq_range|default:"-"|floatformat:3 }}
{% else %}
-
{% endif %}
@@ -287,7 +320,7 @@
<!-- Координаты ГЛ -->
<td>
{% if objitem_data.objitem.geo_obj and objitem_data.objitem.geo_obj.coords %}
{{ objitem_data.objitem.geo_obj.coords.y|floatformat:6 }}, {{ objitem_data.objitem.geo_obj.coords.x|floatformat:6 }}
{{ objitem_data.objitem.geo_obj.coords.y }}, {{ objitem_data.objitem.geo_obj.coords.x }}
{% else %}
-
{% endif %}
@@ -439,70 +472,18 @@ function removeSource(button) {
function updateCounter() {
const rows = document.querySelectorAll('#resultsTable tbody tr');
const counter = document.querySelector('.text-muted');
const counter = document.getElementById('statsCounter');
if (counter) {
// Подсчитываем уникальные источники
const uniqueSources = new Set();
let matchingCount = 0;
rows.forEach(row => {
uniqueSources.add(row.dataset.sourceId);
if (row.dataset.matchesDate === 'true') {
matchingCount++;
}
});
counter.textContent = `Найдено объектов: ${uniqueSources.size}, точек: ${rows.length} (подходящих по дате: ${matchingCount})`;
counter.textContent = `Найдено объектов: ${uniqueSources.size}, точек: ${rows.length}`;
}
}
function filterByDateMatch() {
// Удаляем все строки, которые не подходят по дате
const rows = Array.from(document.querySelectorAll('#resultsTable tbody tr'));
const rowsToRemove = rows.filter(row => row.dataset.matchesDate !== 'true');
if (rowsToRemove.length === 0) {
alert('Все точки уже подходят по дате или фильтр по дате не задан');
return;
}
if (confirm(`Удалить ${rowsToRemove.length} точек, не подходящих по дате?`)) {
// Группируем строки по источникам
const sourceGroups = {};
rows.forEach(row => {
const sourceId = row.dataset.sourceId;
if (!sourceGroups[sourceId]) {
sourceGroups[sourceId] = [];
}
sourceGroups[sourceId].push(row);
});
// Обрабатываем каждый источник отдельно
Object.keys(sourceGroups).forEach(sourceId => {
const sourceRows = sourceGroups[sourceId];
const rowsToKeep = sourceRows.filter(row => row.dataset.matchesDate === 'true');
const rowsToDelete = sourceRows.filter(row => row.dataset.matchesDate !== 'true');
if (rowsToDelete.length === 0) {
return; // Все строки подходят
}
if (rowsToKeep.length === 0) {
// Удаляем все строки источника
rowsToDelete.forEach(row => row.remove());
} else {
// Есть строки для сохранения
// Удаляем строки по одной, используя функцию removeObjItem
rowsToDelete.forEach(row => {
const button = row.querySelector('button[onclick*="removeObjItem"]');
if (button) {
removeObjItem(button);
}
});
}
});
updateCounter();
}
}
function exportToExcel() {
// Собираем ID оставшихся в таблице точек (ObjItem)
@@ -544,6 +525,16 @@ function exportToExcel() {
document.body.removeChild(form);
}
// Функция для выбора/снятия всех опций в select
function selectAllOptions(selectName, selectAll) {
const selectElement = document.querySelector(`select[name="${selectName}"]`);
if (selectElement) {
for (let i = 0; i < selectElement.options.length; i++) {
selectElement.options[i].selected = selectAll;
}
}
}
// Обновляем счетчик при загрузке страницы
document.addEventListener('DOMContentLoaded', function() {
updateCounter();
@@ -563,10 +554,7 @@ document.addEventListener('DOMContentLoaded', function() {
margin-bottom: 0.25rem;
}
/* Выделение строк, подходящих по дате */
.table-success {
background-color: #d1e7dd !important;
}
/* Стили для кнопок действий */
.btn-sm {

View File

@@ -31,49 +31,58 @@ class KubsatView(LoginRequiredMixin, FormView):
form = self.form_class(self.request.GET)
if form.is_valid():
sources = self.apply_filters(form.cleaned_data)
# Определяем, какие источники подходят по дате
date_from = form.cleaned_data.get('date_from')
date_to = form.cleaned_data.get('date_to')
has_date_filter = bool(date_from or date_to)
# Добавляем информацию о соответствии дате для каждого источника
objitem_count = form.cleaned_data.get('objitem_count')
sources_with_date_info = []
for source in sources:
source_data = {
'source': source,
'matches_date': False,
'objitems_data': []
}
# Проверяем каждый ObjItem
for objitem in source.source_objitems.all():
objitem_matches_date = False
objitem_matches_date = True
geo_date = None
if hasattr(objitem, 'geo_obj') and objitem.geo_obj and objitem.geo_obj.timestamp:
geo_date = objitem.geo_obj.timestamp.date()
# Проверяем попадание в диапазон дат
if date_from and date_to:
objitem_matches_date = date_from <= geo_date <= date_to
elif date_from:
objitem_matches_date = geo_date >= date_from
elif date_to:
objitem_matches_date = geo_date <= date_to
else:
objitem_matches_date = True # Нет фильтра по дате
# Проверяем попадание в диапазон дат (только если фильтр задан)
if has_date_filter:
if date_from and date_to:
objitem_matches_date = date_from <= geo_date <= date_to
elif date_from:
objitem_matches_date = geo_date >= date_from
elif date_to:
objitem_matches_date = geo_date <= date_to
elif has_date_filter:
# Если фильтр по дате задан, но у точки нет даты - не подходит
objitem_matches_date = False
source_data['objitems_data'].append({
'objitem': objitem,
'matches_date': objitem_matches_date,
'geo_date': geo_date
})
# Добавляем только точки, подходящие по дате (или все, если фильтр не задан)
if not has_date_filter or objitem_matches_date:
source_data['objitems_data'].append({
'objitem': objitem,
'matches_date': objitem_matches_date,
'geo_date': geo_date
})
# Если хотя бы одна точка подходит по дате, весь источник подходит
if objitem_matches_date:
source_data['matches_date'] = True
# ЭТАП 2: Проверяем количество отфильтрованных точек
filtered_count = len(source_data['objitems_data'])
sources_with_date_info.append(source_data)
# Применяем фильтр по количеству точек (если задан)
include_source = True
if objitem_count:
if objitem_count == '1':
include_source = (filtered_count == 1)
elif objitem_count == '2+':
include_source = (filtered_count >= 2)
if source_data['objitems_data'] and include_source:
sources_with_date_info.append(source_data)
context['sources_with_date_info'] = sources_with_date_info
context['form'] = form
@@ -95,9 +104,11 @@ class KubsatView(LoginRequiredMixin, FormView):
source_objitems__parameter_obj__id_satellite__in=filters['satellites']
).distinct()
# Фильтр по полосе спутника (пока не реализован полностью)
# Фильтр по полосе спутника
if filters.get('band'):
pass # TODO: реализовать фильтр по band
queryset = queryset.filter(
source_objitems__parameter_obj__id_satellite__band__in=filters['band']
).distinct()
# Фильтр по поляризации
if filters.get('polarization'):
@@ -197,7 +208,7 @@ class KubsatExportView(LoginRequiredMixin, FormView):
'Обратный канал, МГц',
'Перенос',
'Получено координат, раз',
'Дата',
'Период получения координат',
'Зеркала',
'СКО, км',
'Примечание',
@@ -283,21 +294,42 @@ class KubsatExportView(LoginRequiredMixin, FormView):
mirrors.append(mirror.name)
mirrors_str = '\n'.join(mirrors)
# Диапазон дат ГЛ (самая ранняя - самая поздняя)
geo_dates = []
for objitem in objitems_list:
if hasattr(objitem, 'geo_obj') and objitem.geo_obj and objitem.geo_obj.timestamp:
geo_dates.append(objitem.geo_obj.timestamp.date())
date_range_str = '-'
if geo_dates:
min_date = min(geo_dates)
max_date = max(geo_dates)
# Форматируем даты в формате d.m.Y
min_date_str = min_date.strftime('%d.%m.%Y')
max_date_str = max_date.strftime('%d.%m.%Y')
if min_date == max_date:
# Если даты совпадают, показываем только одну
date_range_str = min_date_str
else:
# Иначе показываем диапазон
date_range_str = f"{min_date_str}-{max_date_str}"
# Записываем строку
ws.cell(row=row_num, column=1, value=current_date)
ws.cell(row=row_num, column=2, value=latitude)
ws.cell(row=row_num, column=3, value=longitude)
ws.cell(row=row_num, column=4, value=0) # Высота всегда 0
ws.cell(row=row_num, column=4, value=0.0)
ws.cell(row=row_num, column=5, value=location)
ws.cell(row=row_num, column=6, value=satellite_info)
ws.cell(row=row_num, column=7, value=direct_channel)
ws.cell(row=row_num, column=8, value=reverse_channel)
ws.cell(row=row_num, column=9, value=transfer)
ws.cell(row=row_num, column=10, value=objitem_count)
ws.cell(row=row_num, column=11, value='-') # Дата (пока не заполняется)
ws.cell(row=row_num, column=11, value=date_range_str)
ws.cell(row=row_num, column=12, value=mirrors_str)
ws.cell(row=row_num, column=13, value='') # СКО не заполняется
ws.cell(row=row_num, column=14, value='') # Примечание не заполняется
ws.cell(row=row_num, column=13, value='')
ws.cell(row=row_num, column=14, value='')
ws.cell(row=row_num, column=15, value=operator_name)
row_num += 1
@@ -325,6 +357,6 @@ class KubsatExportView(LoginRequiredMixin, FormView):
output.read(),
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)
response['Content-Disposition'] = f'attachment; filename="kubsat_{datetime.now().strftime("%Y%m%d_%H%M%S")}.xlsx"'
response['Content-Disposition'] = f'attachment; filename="kubsat_{datetime.now().strftime("%Y%m%d")}.xlsx"'
return response

View File

@@ -484,7 +484,7 @@ class ObjItemListView(LoginRequiredMixin, View):
"page_obj": page_obj,
"processed_objects": processed_objects,
"items_per_page": items_per_page,
"available_items_per_page": [50, 100, 500, 1000],
"available_items_per_page": [50, 100, 200, 500, 1000],
"freq_min": freq_min,
"freq_max": freq_max,
"range_min": range_min,