451 lines
28 KiB
HTML
451 lines
28 KiB
HTML
{% extends 'mainapp/base.html' %}
|
||
|
||
{% block title %}Главная страница{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container-fluid px-3">
|
||
<div class="row mb-3">
|
||
<div class="col-12">
|
||
<h2>Главная страница</h2>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row g-3">
|
||
<!-- Filters Sidebar -->
|
||
<div class="col-md-3">
|
||
<div class="card h-100">
|
||
<div class="card-body">
|
||
<h5 class="card-title">Фильтры</h5>
|
||
<form method="get" id="filter-form">
|
||
<!-- Display Mode -->
|
||
<div class="mb-3">
|
||
<label class="form-label">Отображение:</label>
|
||
<select name="display_mode" class="form-select form-select-sm" onchange="document.getElementById('filter-form').submit();">
|
||
<option value="sources" {% if display_mode == 'sources' %}selected{% endif %}>Список источников</option>
|
||
<option value="objitems" {% if display_mode == 'objitems' %}selected{% endif %}>Список объектов</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Satellite Selection -->
|
||
<div class="mb-3">
|
||
<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" multiple size="5">
|
||
{% for satellite in satellites %}
|
||
<option value="{{ satellite.id }}"
|
||
{% if satellite.id in selected_satellites %}selected{% endif %}>
|
||
{{ satellite.name }}
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Date Range Filter -->
|
||
<div class="mb-3">
|
||
<label class="form-label">Дата геолокации:</label>
|
||
<input type="date" name="date_from" class="form-control form-control-sm mb-1" placeholder="От" value="{{ date_from }}">
|
||
<input type="date" name="date_to" class="form-control form-control-sm" placeholder="До" value="{{ date_to }}">
|
||
</div>
|
||
|
||
<!-- Frequency Filter -->
|
||
<div class="mb-3">
|
||
<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>
|
||
|
||
<!-- Range Filter -->
|
||
<div class="mb-3">
|
||
<label class="form-label">Полоса, МГц:</label>
|
||
<input type="number" step="0.001" name="range_min" class="form-control form-control-sm mb-1" placeholder="От" value="{{ range_min }}">
|
||
<input type="number" step="0.001" name="range_max" class="form-control form-control-sm" placeholder="До" value="{{ range_max }}">
|
||
</div>
|
||
|
||
<!-- Modulation Filter -->
|
||
<div class="mb-3">
|
||
<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', true)">Все</button>
|
||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="selectAllOptions('modulation', false)">Снять</button>
|
||
</div>
|
||
<select name="modulation" class="form-select form-select-sm" multiple size="4">
|
||
{% for mod in modulations %}
|
||
<option value="{{ mod.id }}"
|
||
{% if mod.id in selected_modulations %}selected{% endif %}>
|
||
{{ mod.name }}
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Polarization Filter -->
|
||
<div class="mb-3">
|
||
<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', true)">Все</button>
|
||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="selectAllOptions('polarization', false)">Снять</button>
|
||
</div>
|
||
<select name="polarization" class="form-select form-select-sm" multiple size="4">
|
||
{% for pol in polarizations %}
|
||
<option value="{{ pol.id }}"
|
||
{% if pol.id in selected_polarizations %}selected{% endif %}>
|
||
{{ pol.name }}
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Marks Filters -->
|
||
<div class="mb-3">
|
||
<label class="form-label">Отображать отметки:</label>
|
||
<div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="show_marks" id="show_marks_0" value="0"
|
||
{% if show_marks == '0' %}checked{% endif %}>
|
||
<label class="form-check-label" for="show_marks_0">Нет</label>
|
||
</div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="show_marks" id="show_marks_1" value="1"
|
||
{% if show_marks == '1' %}checked{% endif %}>
|
||
<label class="form-check-label" for="show_marks_1">Да</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Marks Date Range (shown only if show_marks is enabled) -->
|
||
<div class="mb-3" id="marks-date-filter" style="{% if show_marks != '1' %}display:none;{% endif %}">
|
||
<label class="form-label">Период отметок:</label>
|
||
<input type="date" name="marks_date_from" class="form-control form-control-sm mb-1" placeholder="От" value="{{ marks_date_from }}">
|
||
<input type="date" name="marks_date_to" class="form-control form-control-sm" placeholder="До" value="{{ marks_date_to }}">
|
||
</div>
|
||
|
||
<!-- Marks Status Filter -->
|
||
<div class="mb-3" id="marks-status-filter" style="{% if show_marks != '1' %}display:none;{% endif %}">
|
||
<label class="form-label">Статус отметок:</label>
|
||
<div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="marks_status" id="marks_status_all" value=""
|
||
{% if not marks_status %}checked{% endif %}>
|
||
<label class="form-check-label" for="marks_status_all">Все</label>
|
||
</div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="marks_status" id="marks_status_present" value="present"
|
||
{% if marks_status == 'present' %}checked{% endif %}>
|
||
<label class="form-check-label" for="marks_status_present">✓ Есть</label>
|
||
</div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="marks_status" id="marks_status_absent" value="absent"
|
||
{% if marks_status == 'absent' %}checked{% endif %}>
|
||
<label class="form-check-label" for="marks_status_absent">✗ Нет</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Coordinates Filters -->
|
||
<div class="mb-3">
|
||
<label class="form-label">Координаты геолокации:</label>
|
||
<div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="has_geo" id="has_geo_all" value=""
|
||
{% if not has_geo %}checked{% endif %}>
|
||
<label class="form-check-label" for="has_geo_all">Все</label>
|
||
</div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="has_geo" id="has_geo_1" value="1"
|
||
{% if has_geo == '1' %}checked{% endif %}>
|
||
<label class="form-check-label" for="has_geo_1">Есть</label>
|
||
</div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="has_geo" id="has_geo_0" value="0"
|
||
{% if has_geo == '0' %}checked{% endif %}>
|
||
<label class="form-check-label" for="has_geo_0">Нет</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Координаты Кубсата:</label>
|
||
<div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="has_kupsat" id="has_kupsat_all" value=""
|
||
{% if not has_kupsat %}checked{% endif %}>
|
||
<label class="form-check-label" for="has_kupsat_all">Все</label>
|
||
</div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="has_kupsat" id="has_kupsat_1" value="1"
|
||
{% if has_kupsat == '1' %}checked{% endif %}>
|
||
<label class="form-check-label" for="has_kupsat_1">Есть</label>
|
||
</div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="has_kupsat" id="has_kupsat_0" value="0"
|
||
{% if has_kupsat == '0' %}checked{% endif %}>
|
||
<label class="form-check-label" for="has_kupsat_0">Нет</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Координаты опер. отдела:</label>
|
||
<div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="has_valid" id="has_valid_all" value=""
|
||
{% if not has_valid %}checked{% endif %}>
|
||
<label class="form-check-label" for="has_valid_all">Все</label>
|
||
</div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="has_valid" id="has_valid_1" value="1"
|
||
{% if has_valid == '1' %}checked{% endif %}>
|
||
<label class="form-check-label" for="has_valid_1">Есть</label>
|
||
</div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="radio" name="has_valid" id="has_valid_0" value="0"
|
||
{% if has_valid == '0' %}checked{% endif %}>
|
||
<label class="form-check-label" for="has_valid_0">Нет</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Items Per Page -->
|
||
<div class="mb-3">
|
||
<label for="items-per-page" class="form-label">Элементов на странице:</label>
|
||
<select name="items_per_page" id="items-per-page" class="form-select form-select-sm">
|
||
{% for option in available_items_per_page %}
|
||
<option value="{{ option }}" {% if option == items_per_page %}selected{% endif %}>
|
||
{{ option }}
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Apply Filters and Reset Buttons -->
|
||
<div class="d-grid gap-2">
|
||
<button type="submit" class="btn btn-primary btn-sm">Применить</button>
|
||
<a href="?" class="btn btn-secondary btn-sm">Сбросить</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Main Content -->
|
||
<div class="col-md-9">
|
||
<div class="card h-100">
|
||
<div class="card-body p-0">
|
||
{% if display_mode == 'sources' %}
|
||
<!-- Sources Table -->
|
||
<div class="table-responsive" style="max-height: 75vh; overflow-y: auto;">
|
||
<table class="table table-striped table-hover table-sm table-bordered" style="font-size: 0.85rem;">
|
||
<thead class="table-dark sticky-top">
|
||
<tr>
|
||
<th scope="col">ID</th>
|
||
<th scope="col">Спутники</th>
|
||
<th scope="col">Кол-во объектов</th>
|
||
<th scope="col">Средние координаты</th>
|
||
<th scope="col">Координаты Кубсата</th>
|
||
<th scope="col">Координаты опер. отдела</th>
|
||
<th scope="col">Дата создания</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for source in processed_sources %}
|
||
<tr>
|
||
<td><a href="{% url 'mainapp:source_update' source.id %}">{{ source.id }}</a></td>
|
||
<td>{{ source.satellites }}</td>
|
||
<td>{{ source.objitem_count }}</td>
|
||
<td>{{ source.coords_average }}</td>
|
||
<td>{{ source.coords_kupsat }}</td>
|
||
<td>{{ source.coords_valid }}</td>
|
||
<td>{{ source.created_at|date:"Y-m-d H:i" }}</td>
|
||
</tr>
|
||
{% empty %}
|
||
<tr>
|
||
<td colspan="7" class="text-center py-4">
|
||
Нет данных для выбранных фильтров
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% else %}
|
||
<!-- ObjItems Table -->
|
||
<div class="table-responsive" style="max-height: 75vh; overflow-y: auto;">
|
||
<table class="table table-striped table-hover table-sm table-bordered" style="font-size: 0.85rem;">
|
||
<thead class="table-dark sticky-top">
|
||
<tr>
|
||
<th scope="col">ID</th>
|
||
<th scope="col">Имя</th>
|
||
<th scope="col">Спутник</th>
|
||
<th scope="col">Частота, МГц</th>
|
||
<th scope="col">Полоса, МГц</th>
|
||
<th scope="col">Поляризация</th>
|
||
<th scope="col">Модуляция</th>
|
||
<th scope="col">Сим. v</th>
|
||
<th scope="col">ОСШ</th>
|
||
<th scope="col">Геолокация</th>
|
||
<th scope="col">Дата гео</th>
|
||
<th scope="col">Кубсат</th>
|
||
<th scope="col">Опер. отд</th>
|
||
<th scope="col">Источник</th>
|
||
{% if show_marks == '1' %}
|
||
<th scope="col">Отметки</th>
|
||
{% endif %}
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for item in processed_objitems %}
|
||
<tr>
|
||
<td><a href="{% url 'mainapp:objitem_detail' item.id %}">{{ item.id }}</a></td>
|
||
<td>{{ item.name }}</td>
|
||
<td>{{ item.satellite }}</td>
|
||
<td>{{ item.frequency }}</td>
|
||
<td>{{ item.freq_range }}</td>
|
||
<td>{{ item.polarization }}</td>
|
||
<td>{{ item.modulation }}</td>
|
||
<td>{{ item.bod_velocity }}</td>
|
||
<td>{{ item.snr }}</td>
|
||
<td>{{ item.geo_coords }}</td>
|
||
<td>{{ item.geo_date }}</td>
|
||
<td>{{ item.kupsat_coords }}</td>
|
||
<td>{{ item.valid_coords }}</td>
|
||
<td>
|
||
{% if item.source_id %}
|
||
<a href="{% url 'mainapp:source_update' item.source_id %}">{{ item.source_id }}</a>
|
||
{% else %}
|
||
-
|
||
{% endif %}
|
||
</td>
|
||
{% if show_marks == '1' %}
|
||
<td>
|
||
{% if item.marks %}
|
||
<div class="marks-list">
|
||
{% for mark in item.marks %}
|
||
<div class="mark-item mb-1">
|
||
<span class="badge {% if mark.mark %}bg-success{% else %}bg-danger{% endif %}">
|
||
{% if mark.mark %}✓ Есть{% else %}✗ Нет{% endif %}
|
||
</span>
|
||
<small class="text-muted d-block">
|
||
{{ mark.timestamp|date:"d.m.Y H:i" }}
|
||
</small>
|
||
<small class="text-muted d-block">
|
||
{{ mark.created_by }}
|
||
</small>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% else %}
|
||
<span class="text-muted">-</span>
|
||
{% endif %}
|
||
</td>
|
||
{% endif %}
|
||
</tr>
|
||
{% empty %}
|
||
<tr>
|
||
<td colspan="{% if show_marks == '1' %}15{% else %}14{% endif %}" class="text-center py-4">
|
||
Нет данных для выбранных фильтров
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Pagination -->
|
||
{% if page_obj.paginator.num_pages > 1 %}
|
||
<nav aria-label="Page navigation" class="px-3 pb-3">
|
||
<ul class="pagination justify-content-center mb-0">
|
||
{% if page_obj.has_previous %}
|
||
<li class="page-item">
|
||
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page=1">Первая</a>
|
||
</li>
|
||
<li class="page-item">
|
||
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page={{ page_obj.previous_page_number }}">Предыдущая</a>
|
||
</li>
|
||
{% endif %}
|
||
|
||
{% for num in page_obj.paginator.page_range %}
|
||
{% if page_obj.number == num %}
|
||
<li class="page-item active">
|
||
<span class="page-link">{{ num }}</span>
|
||
</li>
|
||
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
|
||
<li class="page-item">
|
||
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page={{ num }}">{{ num }}</a>
|
||
</li>
|
||
{% endif %}
|
||
{% endfor %}
|
||
|
||
{% if page_obj.has_next %}
|
||
<li class="page-item">
|
||
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page={{ page_obj.next_page_number }}">Следующая</a>
|
||
</li>
|
||
<li class="page-item">
|
||
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page={{ page_obj.paginator.num_pages }}">Последняя</a>
|
||
</li>
|
||
{% endif %}
|
||
</ul>
|
||
</nav>
|
||
{% endif %}
|
||
|
||
<!-- Pagination Info -->
|
||
{% if page_obj %}
|
||
<div class="px-3 pb-3 text-center">
|
||
<small class="text-muted">Показано {{ page_obj.start_index }}-{{ page_obj.end_index }} из {{ page_obj.paginator.count }} записей</small>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<style>
|
||
.marks-list {
|
||
max-height: 200px;
|
||
overflow-y: auto;
|
||
}
|
||
.mark-item {
|
||
padding: 4px;
|
||
border-bottom: 1px solid #e9ecef;
|
||
}
|
||
.mark-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
</style>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// Function to select/deselect all options in a select element
|
||
window.selectAllOptions = function(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;
|
||
}
|
||
}
|
||
};
|
||
|
||
// Toggle marks filters visibility
|
||
const showMarksRadios = document.querySelectorAll('input[name="show_marks"]');
|
||
const marksDateFilter = document.getElementById('marks-date-filter');
|
||
const marksStatusFilter = document.getElementById('marks-status-filter');
|
||
|
||
showMarksRadios.forEach(radio => {
|
||
radio.addEventListener('change', function() {
|
||
if (this.value === '1') {
|
||
marksDateFilter.style.display = 'block';
|
||
marksStatusFilter.style.display = 'block';
|
||
} else {
|
||
marksDateFilter.style.display = 'none';
|
||
marksStatusFilter.style.display = 'none';
|
||
}
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
{% endblock %}
|