Сделал парсер, начал интеграцию с бд
This commit is contained in:
@@ -395,6 +395,18 @@ def show_on_map(modeladmin, request, queryset):
|
||||
|
||||
show_on_map.short_description = "Показать выбранные на карте"
|
||||
|
||||
|
||||
def show_selected_on_map(modeladmin, request, queryset):
|
||||
# Получаем список ID выбранных объектов
|
||||
selected_ids = queryset.values_list('id', flat=True)
|
||||
# Формируем строку вида "1,2,3"
|
||||
ids_str = ','.join(str(pk) for pk in selected_ids)
|
||||
# Перенаправляем на view, который будет отображать карту с выбранными объектами
|
||||
return redirect(reverse('show_selected_objects_map') + f'?ids={ids_str}')
|
||||
|
||||
show_selected_on_map.short_description = "Показать выбранные объекты на карте"
|
||||
show_selected_on_map.icon = 'map'
|
||||
|
||||
class ParameterObjItemInline(admin.StackedInline):
|
||||
model = ObjItem.parameters_obj.through
|
||||
extra = 0
|
||||
@@ -443,7 +455,7 @@ class ObjectAdmin(admin.ModelAdmin):
|
||||
|
||||
ordering = ("name",)
|
||||
inlines = [ParameterObjItemInline, GeoInline]
|
||||
actions = [show_on_map]
|
||||
actions = [show_on_map, show_selected_on_map]
|
||||
readonly_fields = ('created_at', 'created_by', 'updated_at', 'updated_by')
|
||||
|
||||
def get_queryset(self, request):
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
<!-- ВЧ загрузки -->
|
||||
<div class="form-section">
|
||||
<div class="form-section-header d-flex justify-content-between align-items-center">
|
||||
<h4>ВЧ загрузки</h4>
|
||||
<h4>ВЧ загрузка</h4>
|
||||
{% if not parameter_forms.forms.0.instance.pk %}
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" id="add-parameter">Добавить ВЧ загрузку</button>
|
||||
{% endif %}
|
||||
@@ -131,9 +131,8 @@
|
||||
|
||||
<div id="parameters-container">
|
||||
{% for param_form in parameter_forms %}
|
||||
<div class="dynamic-form" data-parameter-index="{{ forloop.counter0 }}">
|
||||
{% comment %} <div class="dynamic-form" data-parameter-index="{{ forloop.counter0 }}"> {% endcomment %}
|
||||
<div class="dynamic-form-header">
|
||||
<h5>ВЧ загрузка #{{ forloop.counter }}</h5>
|
||||
{% if parameter_forms.forms|length > 1 %}
|
||||
<button type="button" class="btn btn-sm btn-outline-danger remove-parameter">Удалить</button>
|
||||
{% endif %}
|
||||
@@ -190,7 +189,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% comment %} </div> {% endcomment %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -365,13 +364,14 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if user.customuser.role == 'admin' or user.customuser.role == 'moderator' %}
|
||||
<div class="d-flex justify-content-end mt-4">
|
||||
<button type="submit" class="btn btn-primary btn-action">Сохранить</button>
|
||||
{% if object %}
|
||||
<a href="{% url 'objitem_delete' object.id %}" class="btn btn-danger btn-action">Удалить</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
{% extends 'mainapp/base.html' %}
|
||||
|
||||
{% block title %}Список объектов{% endblock %}
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.table-responsive table {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.table-responsive tr.selected {
|
||||
background-color: #d4edff;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container-fluid px-3">
|
||||
<div class="row mb-3">
|
||||
@@ -27,14 +37,19 @@
|
||||
|
||||
<!-- Action buttons bar -->
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-success btn-sm" title="Добавить">
|
||||
{% comment %} <button type="button" class="btn btn-success btn-sm" title="Добавить">
|
||||
<i class="bi bi-plus-circle"></i> Добавить
|
||||
</button>
|
||||
<button type="button" class="btn btn-info btn-sm" title="Изменить">
|
||||
<i class="bi bi-pencil"></i> Изменить
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger btn-sm" title="Удалить">
|
||||
<i class="bi bi-trash"></i> Удалить
|
||||
</button> {% endcomment %}
|
||||
{% if user.customuser.role == 'admin' or user.customuser.role == 'moderator' %}
|
||||
<button type="button" class="btn btn-danger btn-sm" title="Удалить" onclick="deleteSelectedObjects()">
|
||||
<i class="bi bi-trash"></i> Удалить
|
||||
</button>
|
||||
{% endif %}
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" title="Показать на карте" onclick="showSelectedOnMap()">
|
||||
<i class="bi bi-map"></i> Карта
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -114,52 +129,57 @@
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="10" checked onchange="toggleColumn(this)"> Геолокация
|
||||
<input type="checkbox" class="column-toggle" data-column="10" checked onchange="toggleColumn(this)"> Местоположение
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="11" checked onchange="toggleColumn(this)"> Кубсат
|
||||
<input type="checkbox" class="column-toggle" data-column="11" checked onchange="toggleColumn(this)"> Геолокация
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="12" checked onchange="toggleColumn(this)"> Опер. отд
|
||||
<input type="checkbox" class="column-toggle" data-column="12" checked onchange="toggleColumn(this)"> Кубсат
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="13" checked onchange="toggleColumn(this)"> Гео-куб, км
|
||||
<input type="checkbox" class="column-toggle" data-column="13" checked onchange="toggleColumn(this)"> Опер. отд
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="14" checked onchange="toggleColumn(this)"> Гео-опер, км
|
||||
<input type="checkbox" class="column-toggle" data-column="14" checked onchange="toggleColumn(this)"> Гео-куб, км
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="15" checked onchange="toggleColumn(this)"> Куб-опер, км
|
||||
<input type="checkbox" class="column-toggle" data-column="15" checked onchange="toggleColumn(this)"> Гео-опер, км
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="16" checked onchange="toggleColumn(this)"> Обновлено
|
||||
<input type="checkbox" class="column-toggle" data-column="16" checked onchange="toggleColumn(this)"> Куб-опер, км
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="17" checked onchange="toggleColumn(this)"> Кем (обновление)
|
||||
<input type="checkbox" class="column-toggle" data-column="17" checked onchange="toggleColumn(this)"> Обновлено
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="18" checked onchange="toggleColumn(this)"> Создано
|
||||
<input type="checkbox" class="column-toggle" data-column="18" checked onchange="toggleColumn(this)"> Кем (обновление)
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="19" checked onchange="toggleColumn(this)"> Кем (создание)
|
||||
<input type="checkbox" class="column-toggle" data-column="19" checked onchange="toggleColumn(this)"> Создано
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="20" checked onchange="toggleColumn(this)"> Кем (создание)
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -230,7 +250,7 @@
|
||||
<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="4">
|
||||
<select name="satellite_id" class="form-select form-select-sm mb-2" multiple size="6">
|
||||
{% for satellite in satellites %}
|
||||
<option value="{{ satellite.id }}"
|
||||
{% if satellite.id in selected_satellites %}selected{% endif %}>
|
||||
@@ -268,8 +288,6 @@
|
||||
<input type="number" step="0.001" name="bod_max" class="form-control form-control-sm" placeholder="До" value="{{ bod_max|default:'' }}">
|
||||
</div>
|
||||
|
||||
<!-- Removed old search input as it's now in the toolbar -->
|
||||
|
||||
<!-- Modulation Filter -->
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Модуляция:</label>
|
||||
@@ -277,7 +295,7 @@
|
||||
<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 mb-2" multiple size="4">
|
||||
<select name="modulation" class="form-select form-select-sm mb-2" multiple size="6">
|
||||
{% for mod in modulations %}
|
||||
<option value="{{ mod.id }}"
|
||||
{% if mod.id in selected_modulations %}selected{% endif %}>
|
||||
@@ -431,7 +449,7 @@
|
||||
{% if sort == 'geo_timestamp' %} <i class="bi bi-sort-up ms-1"></i> <a href="?{% for key, value in request.GET.items %}{% if key != 'page' and key != 'sort' %}{{ key }}={{ value }}&{% endif %}{% endfor %}" class="text-white ms-1"><i class="bi bi-x-lg"></i></a> {% elif sort == '-geo_timestamp' %} <i class="bi bi-sort-down ms-1"></i> <a href="?{% for key, value in request.GET.items %}{% if key != 'page' and key != 'sort' %}{{ key }}={{ value }}&{% endif %}{% endfor %}" class="text-white ms-1"><i class="bi bi-x-lg"></i></a> {% else %} <i class="bi bi-arrow-down-up ms-1"></i> {% endif %}
|
||||
</a>
|
||||
</th>
|
||||
|
||||
<th scope="col">Местоположение</th>
|
||||
<!-- Столбец "Геолокация" - без сортировки -->
|
||||
<th scope="col">Геолокация</th>
|
||||
|
||||
@@ -488,6 +506,7 @@
|
||||
<td>{{ item.modulation }}</td>
|
||||
<td>{{ item.snr }}</td>
|
||||
<td>{{ item.geo_timestamp|date:"d.m.Y H:i" }}</td>
|
||||
<td>{{ item.geo_location}}</td>
|
||||
<td>{{ item.geo_coords }}</td>
|
||||
<td>{{ item.kupsat_coords }}</td>
|
||||
<td>{{ item.valid_coords }}</td>
|
||||
@@ -521,11 +540,149 @@
|
||||
|
||||
|
||||
<script>
|
||||
let lastCheckedIndex = null;
|
||||
|
||||
function updateRowHighlight(checkbox) {
|
||||
const row = checkbox.closest('tr');
|
||||
if (checkbox.checked) {
|
||||
row.classList.add('selected');
|
||||
} else {
|
||||
row.classList.remove('selected');
|
||||
}
|
||||
}
|
||||
|
||||
function handleCheckboxClick(e) {
|
||||
if (e.shiftKey && lastCheckedIndex !== null) {
|
||||
const checkboxes = document.querySelectorAll('.item-checkbox');
|
||||
const currentIndex = Array.from(checkboxes).indexOf(e.target);
|
||||
const startIndex = Math.min(lastCheckedIndex, currentIndex);
|
||||
const endIndex = Math.max(lastCheckedIndex, currentIndex);
|
||||
|
||||
for (let i = startIndex; i <= endIndex; i++) {
|
||||
checkboxes[i].checked = e.target.checked;
|
||||
updateRowHighlight(checkboxes[i]);
|
||||
}
|
||||
} else {
|
||||
updateRowHighlight(e.target);
|
||||
}
|
||||
lastCheckedIndex = Array.from(document.querySelectorAll('.item-checkbox')).indexOf(e.target);
|
||||
}
|
||||
|
||||
// Function to show selected objects on map
|
||||
function showSelectedOnMap() {
|
||||
// Get all checked checkboxes
|
||||
const checkedCheckboxes = document.querySelectorAll('.item-checkbox:checked');
|
||||
|
||||
if (checkedCheckboxes.length === 0) {
|
||||
alert('Пожалуйста, выберите хотя бы один объект для отображения на карте');
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract IDs from checked checkboxes
|
||||
const selectedIds = [];
|
||||
checkedCheckboxes.forEach(checkbox => {
|
||||
selectedIds.push(checkbox.value);
|
||||
});
|
||||
|
||||
// Redirect to the map view with selected IDs as query parameter
|
||||
const url = '{% url "show_selected_objects_map" %}' + '?ids=' + selectedIds.join(',');
|
||||
window.open(url, '_blank'); // Open in a new tab
|
||||
}
|
||||
|
||||
function clearSelections() {
|
||||
const itemCheckboxes = document.querySelectorAll('.item-checkbox');
|
||||
itemCheckboxes.forEach(checkbox => {
|
||||
checkbox.checked = false;
|
||||
});
|
||||
// Also uncheck the select-all checkbox if it exists
|
||||
const selectAllCheckbox = document.getElementById('select-all');
|
||||
if (selectAllCheckbox) {
|
||||
selectAllCheckbox.checked = false;
|
||||
}
|
||||
// Remove selected class from rows
|
||||
const selectedRows = document.querySelectorAll('tr.selected');
|
||||
selectedRows.forEach(row => {
|
||||
row.classList.remove('selected');
|
||||
});
|
||||
}
|
||||
|
||||
// Function to delete selected objects
|
||||
function deleteSelectedObjects() {
|
||||
// Get all checked checkboxes
|
||||
const checkedCheckboxes = document.querySelectorAll('.item-checkbox:checked');
|
||||
|
||||
if (checkedCheckboxes.length === 0) {
|
||||
alert('Пожалуйста, выберите хотя бы один объект для удаления');
|
||||
return;
|
||||
}
|
||||
|
||||
// Confirm deletion with user
|
||||
if (!confirm(`Вы уверены, что хотите удалить ${checkedCheckboxes.length} объект(ов)?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract IDs from checked checkboxes
|
||||
const selectedIds = [];
|
||||
checkedCheckboxes.forEach(checkbox => {
|
||||
selectedIds.push(checkbox.value);
|
||||
});
|
||||
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
// Does this cookie string begin with the name we want?
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
const csrftoken = getCookie('csrftoken');
|
||||
|
||||
|
||||
// Prepare request headers
|
||||
const headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
};
|
||||
|
||||
// Add CSRF token to headers only if it exists
|
||||
if (csrftoken) {
|
||||
headers['X-CSRFToken'] = csrftoken;
|
||||
}
|
||||
|
||||
// Send AJAX request to delete selected objects
|
||||
fetch('{% url "delete_selected_objects" %}', {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: 'ids=' + selectedIds.join(',')
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert(data.message);
|
||||
clearSelections();
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Ошибка: ' + (data.error || 'Неизвестная ошибка'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('Произошла ошибка при удалении объектов');
|
||||
});
|
||||
}
|
||||
|
||||
// Остальной ваш JavaScript код остается без изменений
|
||||
function toggleColumn(checkbox) {
|
||||
const columnIndex = parseInt(checkbox.getAttribute('data-column'));
|
||||
const table = document.querySelector('.table');
|
||||
const cells = table.querySelectorAll(`td:nth-child(${columnIndex + 1}), th:nth-child(${columnIndex + 1})`);
|
||||
|
||||
|
||||
if (checkbox.checked) {
|
||||
cells.forEach(cell => {
|
||||
cell.style.display = '';
|
||||
@@ -536,7 +693,6 @@ function toggleColumn(checkbox) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAllColumns(selectAllCheckbox) {
|
||||
const columnCheckboxes = document.querySelectorAll('.column-toggle');
|
||||
columnCheckboxes.forEach(checkbox => {
|
||||
@@ -545,41 +701,31 @@ function toggleAllColumns(selectAllCheckbox) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const selectAllCheckbox = document.getElementById('select-all');
|
||||
const itemCheckboxes = document.querySelectorAll('.item-checkbox');
|
||||
|
||||
|
||||
if (selectAllCheckbox && itemCheckboxes.length > 0) {
|
||||
selectAllCheckbox.addEventListener('change', function() {
|
||||
itemCheckboxes.forEach(checkbox => {
|
||||
checkbox.checked = selectAllCheckbox.checked;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
itemCheckboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('change', function() {
|
||||
const allChecked = Array.from(itemCheckboxes).every(cb => cb.checked);
|
||||
selectAllCheckbox.checked = allChecked;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Handle multiple selection for modulations and polarizations
|
||||
const modulationSelect = document.querySelector('select[name="modulation"]');
|
||||
const polarizationSelect = document.querySelector('select[name="polarization"]');
|
||||
|
||||
// Prevent deselecting all options when Ctrl+click is used
|
||||
if (modulationSelect) {
|
||||
modulationSelect.addEventListener('change', function(e) {
|
||||
document.getElementById('filter-form').submit();
|
||||
});
|
||||
}
|
||||
|
||||
if (polarizationSelect) {
|
||||
polarizationSelect.addEventListener('change', function(e) {
|
||||
document.getElementById('filter-form').submit();
|
||||
|
||||
// Добавляем обработчик для выбора диапазона
|
||||
itemCheckboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('click', handleCheckboxClick);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Handle kubsat and valid coords checkboxes (mutually exclusive)
|
||||
// Add a function to handle radio-like behavior for these checkboxes
|
||||
@@ -597,14 +743,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
} else {
|
||||
// If both are unchecked, no action needed
|
||||
}
|
||||
document.getElementById('filter-form').submit();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
setupRadioLikeCheckboxes('has_kupsat');
|
||||
setupRadioLikeCheckboxes('has_valid');
|
||||
|
||||
|
||||
// Function to select/deselect all options in a select element
|
||||
window.selectAllOptions = function(selectName, selectAll) {
|
||||
const selectElement = document.querySelector(`select[name="${selectName}"]`);
|
||||
@@ -612,49 +757,40 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
for (let i = 0; i < selectElement.options.length; i++) {
|
||||
selectElement.options[i].selected = selectAll;
|
||||
}
|
||||
document.getElementById('filter-form').submit();
|
||||
}
|
||||
};
|
||||
|
||||
// Function to update the page when satellite selection changes
|
||||
function updateSatelliteSelection() {
|
||||
document.getElementById('filter-form').submit();
|
||||
}
|
||||
|
||||
|
||||
// Get all current filter values and return as URL parameters
|
||||
function getAllFilterParams() {
|
||||
const form = document.getElementById('filter-form');
|
||||
const searchValue = document.getElementById('toolbar-search').value;
|
||||
|
||||
|
||||
// Create URLSearchParams object from the form
|
||||
const params = new URLSearchParams(new FormData(form));
|
||||
|
||||
|
||||
// Add search value from toolbar if present
|
||||
if (searchValue.trim() !== '') {
|
||||
params.set('search', searchValue);
|
||||
} else {
|
||||
// Remove search parameter if empty
|
||||
params.delete('search');
|
||||
}
|
||||
|
||||
|
||||
return params.toString();
|
||||
}
|
||||
|
||||
|
||||
// Function to perform search
|
||||
window.performSearch = function() {
|
||||
const filterParams = getAllFilterParams();
|
||||
window.location.search = filterParams;
|
||||
};
|
||||
|
||||
|
||||
// Function to clear search
|
||||
window.clearSearch = function() {
|
||||
// Clear only the search input in the toolbar
|
||||
document.getElementById('toolbar-search').value = '';
|
||||
// Submit the form to update the results
|
||||
const filterParams = getAllFilterParams();
|
||||
window.location.search = filterParams;
|
||||
};
|
||||
|
||||
|
||||
// Handle Enter key in toolbar search
|
||||
const toolbarSearch = document.getElementById('toolbar-search');
|
||||
if (toolbarSearch) {
|
||||
@@ -664,58 +800,55 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add event listener to satellite select for immediate update
|
||||
const satelliteSelect = document.querySelector('select[name="satellite_id"]');
|
||||
if (satelliteSelect) {
|
||||
satelliteSelect.addEventListener('change', function() {
|
||||
updateSatelliteSelection();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//const satelliteSelect = document.querySelector('select[name="satellite_id"]');
|
||||
//if (satelliteSelect) {
|
||||
//satelliteSelect.addEventListener('change', function() {
|
||||
// updateSatelliteSelection();
|
||||
// });
|
||||
//}
|
||||
|
||||
// Function to update items per page
|
||||
window.updateItemsPerPage = function() {
|
||||
const itemsPerPageSelect = document.getElementById('items-per-page');
|
||||
const currentParams = new URLSearchParams(window.location.search);
|
||||
|
||||
|
||||
// Add or update the items_per_page parameter
|
||||
currentParams.set('items_per_page', itemsPerPageSelect.value);
|
||||
|
||||
|
||||
// Remove page parameter to reset to first page when changing items per page
|
||||
currentParams.delete('page');
|
||||
|
||||
|
||||
// Update URL and reload
|
||||
window.location.search = currentParams.toString();
|
||||
};
|
||||
|
||||
|
||||
// Initialize column visibility - hide creation columns by default
|
||||
function initColumnVisibility() {
|
||||
const creationDateCheckbox = document.querySelector('input[data-column="18"]');
|
||||
const creationUserCheckbox = document.querySelector('input[data-column="19"]');
|
||||
const creationDistanceGOpCheckbox = document.querySelector('input[data-column="14"]');
|
||||
const creationDistanceKubOpCheckbox = document.querySelector('input[data-column="15"]');
|
||||
|
||||
const creationDateCheckbox = document.querySelector('input[data-column="19"]');
|
||||
const creationUserCheckbox = document.querySelector('input[data-column="20"]');
|
||||
const creationDistanceGOpCheckbox = document.querySelector('input[data-column="15"]');
|
||||
const creationDistanceKubOpCheckbox = document.querySelector('input[data-column="16"]');
|
||||
if (creationDistanceGOpCheckbox) {
|
||||
creationDistanceGOpCheckbox.checked = false;
|
||||
creationDistanceGOpCheckbox.checked = false;
|
||||
toggleColumn(creationDistanceGOpCheckbox);
|
||||
}
|
||||
|
||||
if (creationDistanceKubOpCheckbox) {
|
||||
creationDistanceKubOpCheckbox.checked = false;
|
||||
creationDistanceKubOpCheckbox.checked = false;
|
||||
toggleColumn(creationDistanceKubOpCheckbox);
|
||||
}
|
||||
|
||||
|
||||
if (creationDateCheckbox) {
|
||||
creationDateCheckbox.checked = false;
|
||||
creationDateCheckbox.checked = false;
|
||||
toggleColumn(creationDateCheckbox);
|
||||
}
|
||||
|
||||
|
||||
if (creationUserCheckbox) {
|
||||
creationUserCheckbox.checked = false;
|
||||
creationUserCheckbox.checked = false;
|
||||
toggleColumn(creationUserCheckbox);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Initialize column visibility after page loads
|
||||
setTimeout(initColumnVisibility, 100); // Slight delay to ensure DOM is fully loaded
|
||||
});
|
||||
|
||||
106
dbapp/mainapp/templates/mainapp/objitem_map.html
Normal file
106
dbapp/mainapp/templates/mainapp/objitem_map.html
Normal file
@@ -0,0 +1,106 @@
|
||||
{% extends "mapsapp/map2d_base.html" %}
|
||||
{% load static %}
|
||||
{% block title %}Карта выбранных объектов{% endblock title %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
// Цвета для стандартных маркеров (из leaflet-color-markers)
|
||||
var markerColors = ['red', 'green', 'orange', 'violet', 'grey', 'black', 'blue'];
|
||||
var getColorIcon = function(color) {
|
||||
return L.icon({
|
||||
iconUrl: '{% static "leaflet-markers/img/marker-icon-" %}' + color + '.png',
|
||||
shadowUrl: '{% static "leaflet-markers/img/marker-shadow.png" %}',
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
popupAnchor: [1, -34],
|
||||
shadowSize: [41, 41]
|
||||
});
|
||||
};
|
||||
|
||||
var overlays = [];
|
||||
|
||||
{% for group in groups %}
|
||||
var groupIndex = {{ forloop.counter0 }};
|
||||
var colorName = markerColors[groupIndex % markerColors.length];
|
||||
var groupIcon = getColorIcon(colorName);
|
||||
|
||||
var groupLayer = L.layerGroup();
|
||||
|
||||
var subgroup = [];
|
||||
{% for point_data in group.points %}
|
||||
var pointName = "{{ group.name|escapejs }}";
|
||||
|
||||
var marker = L.marker([{{ point_data.point.1|safe }}, {{ point_data.point.0|safe }}], {
|
||||
icon: groupIcon
|
||||
}).bindPopup(pointName + '<br>' + "{{ point_data.frequency|escapejs }}");
|
||||
|
||||
groupLayer.addLayer(marker);
|
||||
|
||||
subgroup.push({
|
||||
label: "{{ forloop.counter }} - {{ point_data.frequency }}",
|
||||
layer: marker
|
||||
});
|
||||
{% endfor %}
|
||||
|
||||
overlays.push({
|
||||
label: '{{ group.name|escapejs }}',
|
||||
selectAllCheckbox: true,
|
||||
children: subgroup,
|
||||
layer: groupLayer
|
||||
});
|
||||
{% endfor %}
|
||||
|
||||
// Create the layer control with a custom container that includes a select all checkbox
|
||||
var layerControl = L.control.layers.tree(baseLayers, overlays, {
|
||||
collapsed: false,
|
||||
autoZIndex: true
|
||||
});
|
||||
|
||||
// Add the layer control to the map
|
||||
layerControl.addTo(map);
|
||||
|
||||
// Calculate map bounds to fit all markers
|
||||
{% if groups %}
|
||||
var groupBounds = L.featureGroup([]);
|
||||
{% for group in groups %}
|
||||
{% for point_data in group.points %}
|
||||
groupBounds.addLayer(L.marker([{{ point_data.point.1|safe }}, {{ point_data.point.0|safe }}]));
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
map.fitBounds(groupBounds.getBounds().pad(0.1)); // Add some padding
|
||||
{% else %}
|
||||
map.setView([55.75, 37.62], 5); // Default view if no markers
|
||||
{% endif %}
|
||||
|
||||
// Add a "Select All" checkbox functionality for all overlays
|
||||
setTimeout(function() {
|
||||
// Create a custom "select all" checkbox
|
||||
var selectAllContainer = document.createElement('div');
|
||||
selectAllContainer.className = 'leaflet-control-layers-select-all';
|
||||
selectAllContainer.style.padding = '5px';
|
||||
selectAllContainer.style.borderBottom = '1px solid #ccc';
|
||||
selectAllContainer.style.marginBottom = '5px';
|
||||
selectAllContainer.innerHTML = '<label><input type="checkbox" id="select-all-overlays" checked> Показать все точки</label>';
|
||||
|
||||
// Insert the checkbox at the top of the layer control
|
||||
var layerControlContainer = document.querySelector('.leaflet-control-layers-list');
|
||||
if (layerControlContainer) {
|
||||
layerControlContainer.insertBefore(selectAllContainer, layerControlContainer.firstChild);
|
||||
}
|
||||
|
||||
// Add event listener to the "select all" checkbox
|
||||
document.getElementById('select-all-overlays').addEventListener('change', function() {
|
||||
var isChecked = this.checked;
|
||||
|
||||
// Iterate through all overlays and toggle visibility
|
||||
for (var i = 0; i < overlays.length; i++) {
|
||||
if (isChecked) {
|
||||
map.addLayer(overlays[i].layer);
|
||||
} else {
|
||||
map.removeLayer(overlays[i].layer);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 500); // Slight delay to ensure the tree control has been rendered
|
||||
</script>
|
||||
{% endblock extra_js %}
|
||||
@@ -14,6 +14,8 @@ urlpatterns = [
|
||||
path('transponders', views.AddTranspondersView.as_view(), name='add_trans'),
|
||||
path('csv-data', views.LoadCsvDataView.as_view(), name='load_csv_data'),
|
||||
path('map-points/', views.ShowMapView.as_view(), name='admin_show_map'),
|
||||
path('show-selected-objects-map/', views.ShowSelectedObjectsMapView.as_view(), name='show_selected_objects_map'),
|
||||
path('delete-selected-objects/', views.DeleteSelectedObjectsView.as_view(), name='delete_selected_objects'),
|
||||
path('cluster/', views.ClusterTestView.as_view(), name='cluster'),
|
||||
path('vch-upload/', views.UploadVchLoadView.as_view(), name='vch_load'),
|
||||
path('vch-link/', views.LinkVchSigmaView.as_view(), name='link_vch_sigma'),
|
||||
|
||||
@@ -228,6 +228,52 @@ class ShowMapView(UserPassesTestMixin, View):
|
||||
return render(request, 'admin/map_custom.html', context)
|
||||
|
||||
|
||||
class ShowSelectedObjectsMapView(LoginRequiredMixin, View):
|
||||
def get(self, request):
|
||||
ids = request.GET.get('ids', '')
|
||||
points = []
|
||||
if ids:
|
||||
id_list = [int(x) for x in ids.split(',') if x.isdigit()]
|
||||
locations = ObjItem.objects.filter(id__in=id_list).prefetch_related(
|
||||
'parameters_obj__id_satellite',
|
||||
'parameters_obj__polarization',
|
||||
'parameters_obj__modulation',
|
||||
'parameters_obj__standard',
|
||||
'geo_obj'
|
||||
)
|
||||
for obj in locations:
|
||||
param = obj.parameters_obj.get()
|
||||
points.append({
|
||||
'name': f"{obj.name}",
|
||||
'freq': f"{param.frequency} [{param.freq_range}] МГц",
|
||||
'point': (obj.geo_obj.coords.x, obj.geo_obj.coords.y)
|
||||
})
|
||||
else:
|
||||
return redirect('objitem_list')
|
||||
|
||||
# Group points by object name
|
||||
from collections import defaultdict
|
||||
grouped = defaultdict(list)
|
||||
for p in points:
|
||||
grouped[p["name"]].append({
|
||||
'point': p["point"],
|
||||
'frequency': p["freq"]
|
||||
})
|
||||
|
||||
groups = [
|
||||
{
|
||||
"name": name,
|
||||
"points": coords_list
|
||||
}
|
||||
for name, coords_list in grouped.items()
|
||||
]
|
||||
|
||||
context = {
|
||||
'groups': groups,
|
||||
}
|
||||
return render(request, 'mainapp/objitem_map.html', context)
|
||||
|
||||
|
||||
class ClusterTestView(LoginRequiredMixin, View):
|
||||
def get(self, request):
|
||||
objs = ObjItem.objects.filter(name__icontains="! Astra 4A 12654,040 [1,962] МГц H")
|
||||
@@ -316,6 +362,28 @@ class ProcessKubsatView(LoginRequiredMixin, FormView):
|
||||
messages.error(self.request, "Форма заполнена некорректно.")
|
||||
return super().form_invalid(form)
|
||||
|
||||
|
||||
class DeleteSelectedObjectsView(LoginRequiredMixin, View):
|
||||
def post(self, request):
|
||||
if request.user.customuser.role not in ['admin', 'moderator']:
|
||||
return JsonResponse({'error': 'У вас нет прав для удаления объектов'}, status=403)
|
||||
|
||||
ids = request.POST.get('ids', '')
|
||||
if not ids:
|
||||
return JsonResponse({'error': 'Нет ID для удаления'}, status=400)
|
||||
|
||||
try:
|
||||
id_list = [int(x) for x in ids.split(',') if x.isdigit()]
|
||||
deleted_count, _ = ObjItem.objects.filter(id__in=id_list).delete()
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'message': 'Объект успешно удалён',
|
||||
# 'deleted_count': deleted_count
|
||||
})
|
||||
except Exception as e:
|
||||
return JsonResponse({'error': f'Ошибка при удалении: {str(e)}'}, status=500)
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
|
||||
class ObjItemListView(LoginRequiredMixin, View):
|
||||
@@ -520,6 +588,8 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
param = param_list[0]
|
||||
|
||||
geo_coords = "-"
|
||||
geo_timestamp = "-"
|
||||
geo_location = "-"
|
||||
kupsat_coords = "-"
|
||||
valid_coords = "-"
|
||||
distance_geo_kup = "-"
|
||||
@@ -528,6 +598,8 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
|
||||
if obj.geo_obj:
|
||||
geo_timestamp = obj.geo_obj.timestamp
|
||||
geo_location = obj.geo_obj.location
|
||||
|
||||
if obj.geo_obj.coords:
|
||||
longitude = obj.geo_obj.coords.coords[0]
|
||||
latitude = obj.geo_obj.coords.coords[1]
|
||||
@@ -596,6 +668,7 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
'modulation': modulation_name,
|
||||
'snr': snr,
|
||||
'geo_timestamp': geo_timestamp,
|
||||
'geo_location': geo_location,
|
||||
'geo_coords': geo_coords,
|
||||
'kupsat_coords': kupsat_coords,
|
||||
'valid_coords': valid_coords,
|
||||
|
||||
Reference in New Issue
Block a user