Добавил форму с предосмотром и редактированием

This commit is contained in:
2025-11-01 13:57:50 +03:00
parent e01785fa53
commit c8a951eac6
10 changed files with 1058 additions and 50 deletions

View File

@@ -110,7 +110,7 @@ class GeoInline(admin.StackedInline):
fieldsets = (
("Основная информация", {
"fields": ("mirrors", "location", "distance_coords_kup",
"distance_coords_valid", "distance_kup_valid", "timestamp", "comment", "id_user_add")
"distance_coords_valid", "distance_kup_valid", "timestamp", "comment",)
}),
("Координаты: геолокация", {
"fields": ("longitude_geo", "latitude_geo", "coords"),
@@ -195,15 +195,6 @@ class ParameterAdmin(ImportExportActionModelAdmin, admin.ModelAdmin):
"standard",
"sigma_parameter"
)
# fields = ( "id_satellite",
# "frequency",
# "freq_range",
# "polarization",
# "modulation",
# "bod_velocity",
# "snr",
# "standard",
# "id_sigma_parameter")
list_display_links = ("frequency", "id_satellite", )
list_filter = (
HasSigmaParameterFilter,
@@ -309,7 +300,7 @@ class GeoAdmin(ImportExportActionModelAdmin, LeafletGeoAdmin):
fieldsets = (
("Основная информация", {
"fields": ("mirrors", "location", "distance_coords_kup",
"distance_coords_valid", "distance_kup_valid", "timestamp", "comment", "id_user_add")
"distance_coords_valid", "distance_kup_valid", "timestamp", "comment",)
}),
("Координаты: геолокация", {
"fields": ("longitude_geo", "latitude_geo", "coords"),
@@ -337,7 +328,6 @@ class GeoAdmin(ImportExportActionModelAdmin, LeafletGeoAdmin):
"is_average",
("location", MultiSelectDropdownFilter),
("timestamp", DateRangeQuickSelectListFilterBuilder()),
("id_user_add", MultiSelectRelatedDropdownFilter),
)
search_fields = (
"mirrors__name",
@@ -346,7 +336,6 @@ class GeoAdmin(ImportExportActionModelAdmin, LeafletGeoAdmin):
"coords_kupsat",
"coords_valid"
)
list_select_related = ("id_user_add", )
prefetch_related = ("mirrors", )
@@ -467,7 +456,6 @@ class ObjectAdmin(admin.ModelAdmin):
)
def get_readonly_fields(self, request, obj=None):
# Always make these fields readonly to preserve tracking
return self.readonly_fields
def save_model(self, request, obj, form, change):

View File

@@ -1,5 +1,5 @@
from django import forms
from .models import Satellite, Polarization
from .models import Satellite, Polarization, ObjItem, Parameter, Geo, Modulation, Standard
class UploadFileForm(forms.Form):
file = forms.FileField(
@@ -104,3 +104,45 @@ class NewEventForm(forms.Form):
'accept': '.xlsx,.xls'
})
)
class ParameterForm(forms.ModelForm):
class Meta:
model = Parameter
fields = [
'id_satellite', 'frequency', 'freq_range', 'polarization',
'bod_velocity', 'modulation', 'snr', 'standard'
]
widgets = {
'id_satellite': forms.Select(attrs={'class': 'form-select'}, choices=[]),
'frequency': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.001'}),
'freq_range': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.001'}),
'bod_velocity': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.001'}),
'snr': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.001'}),
'polarization': forms.Select(attrs={'class': 'form-select'}, choices=[]),
'modulation': forms.Select(attrs={'class': 'form-select'}, choices=[]),
'standard': forms.Select(attrs={'class': 'form-select'}, choices=[]),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['id_satellite'].choices = [(s.id, s.name) for s in Satellite.objects.all()]
self.fields['polarization'].choices = [(p.id, p.name) for p in Polarization.objects.all()]
self.fields['modulation'].choices = [(m.id, m.name) for m in Modulation.objects.all()]
self.fields['standard'].choices = [(s.id, s.name) for s in Standard.objects.all()]
class GeoForm(forms.ModelForm):
class Meta:
model = Geo
fields = ['location', 'comment', 'is_average']
widgets = {
'location': forms.TextInput(attrs={'class': 'form-control'}),
'comment': forms.TextInput(attrs={'class': 'form-control'}),
'is_average': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
}
class ObjItemForm(forms.ModelForm):
class Meta:
model = ObjItem
fields = ['name']
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
}

View File

@@ -0,0 +1,25 @@
# Generated by Django 5.2.7 on 2025-11-01 07:38
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('mainapp', '0003_alter_objitem_created_at_alter_objitem_updated_at'),
]
operations = [
migrations.RemoveField(
model_name='geo',
name='id_user_add',
),
migrations.RemoveField(
model_name='objitem',
name='id_user_add',
),
migrations.RemoveField(
model_name='parameter',
name='id_user_add',
),
]

View File

@@ -165,7 +165,7 @@ class Parameter(models.Model):
standard = models.ForeignKey(
Standard, default=get_default_standard, on_delete=models.SET_DEFAULT, related_name="standards", null=True, blank=True, verbose_name="Стандарт"
)
id_user_add = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name="parameter_added", verbose_name="Пользователь", null=True, blank=True)
# id_user_add = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name="parameter_added", verbose_name="Пользователь", null=True, blank=True)
objitems = models.ManyToManyField(ObjItem, related_name="parameters_obj", verbose_name="Источники", blank=True)
# id_sigma_parameter = models.ManyToManyField(SigmaParameter, on_delete=models.SET_NULL, related_name="sigma_parameter", verbose_name="ВЧ с sigma", null=True, blank=True)
# id_sigma_parameter = models.ManyToManyField(SigmaParameter, verbose_name="ВЧ с sigma", null=True, blank=True)
@@ -261,7 +261,7 @@ class Geo(models.Model):
coords_kupsat = gis.PointField(srid=4326, null=True, blank=True, verbose_name="Координаты Кубсата")
coords_valid = gis.PointField(srid=4326, null=True, blank=True, verbose_name="Координаты оперативников")
is_average = models.BooleanField(null=True, blank=True, verbose_name="Усреднённое")
id_user_add = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name="geos_added", verbose_name="Пользователь", null=True, blank=True)
# id_user_add = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name="geos_added", verbose_name="Пользователь", null=True, blank=True)
distance_coords_kup = models.GeneratedField(
expression=functions.Distance("coords", "coords_kupsat")/1000,
output_field=models.FloatField(),
@@ -280,7 +280,7 @@ class Geo(models.Model):
db_persist=True,
null=True, blank=True, verbose_name="Расстояние между купсатом и оперативным отделом, км"
)
objitem = models.OneToOneField(ObjItem, on_delete=models.SET_NULL, verbose_name="Гео", related_name="geo_obj", null=True)
objitem = models.OneToOneField(ObjItem, on_delete=models.CASCADE, verbose_name="Гео", related_name="geo_obj", null=True)
def __str__(self):
longitude = self.coords.coords[0]

View File

@@ -0,0 +1,25 @@
{% 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>Удалить объект "{{ object }}"?</h2>
</div>
</div>
<div class="card">
<div class="card-body">
<form method="post">
{% csrf_token %}
<p>Вы уверены, что хотите удалить этот объект? Это действие нельзя будет отменить.</p>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-danger">Удалить</button>
<a href="{% url 'home' %}" class="btn btn-secondary ms-2">Отмена</a>
</div>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,513 @@
{% extends 'mainapp/base.html' %}
{% load static %}
{% load static leaflet_tags %}
{% block title %}{% if object %}Редактировать объект: {{ object.name }}{% else %}Создать объект{% endif %}{% endblock %}
{% block extra_css %}
<style>
.form-section { margin-bottom: 2rem; border: 1px solid #dee2e6; border-radius: 0.25rem; padding: 1rem; }
.form-section-header { border-bottom: 1px solid #dee2e6; padding-bottom: 0.5rem; margin-bottom: 1rem; }
.btn-action { margin-right: 0.5rem; }
.dynamic-form { border: 1px dashed #ced4da; padding: 1rem; margin-top: 1rem; border-radius: 0.25rem; }
.dynamic-form-header { display: flex; justify-content: space-between; align-items: center; }
.readonly-field { background-color: #f8f9fa; padding: 0.375rem 0.75rem; border: 1px solid #ced4da; border-radius: 0.25rem; }
.coord-group { border: 1px solid #dee2e6; padding: 0.75rem; border-radius: 0.25rem; margin-bottom: 1rem; }
.coord-group-header { font-weight: bold; margin-bottom: 0.5rem; }
.form-check-input { margin-top: 0.25rem; }
.datetime-group { display: flex; gap: 1rem; }
.datetime-group > div { flex: 1; }
#map { height: 500px; width: 100%; margin-bottom: 1rem; }
.map-container { margin-bottom: 1rem; }
.coord-sync-group { border: 1px solid #dee2e6; padding: 0.75rem; border-radius: 0.25rem; }
.map-controls {
display: flex;
gap: 10px;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.map-control-btn {
padding: 0.375rem 0.75rem;
border: 1px solid #ced4da;
background-color: #f8f9fa;
border-radius: 0.25rem;
cursor: pointer;
}
.map-control-btn.active {
background-color: #e9ecef;
border-color: #dee2e6;
}
.map-control-btn.edit {
background-color: #fff3cd;
border-color: #ffeeba;
}
.map-control-btn.save {
background-color: #d1ecf1;
border-color: #bee5eb;
}
.map-control-btn.cancel {
background-color: #f8d7da;
border-color: #f5c6cb;
}
.leaflet-marker-icon {
filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.3));
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid px-3">
<div class="row mb-3">
<div class="col-12 d-flex justify-content-between align-items-center">
<h2>{% if object %}Редактировать объект: {{ object.name }}{% else %}Создать объект{% endif %}</h2>
<div>
<a href="{% url 'home' %}" class="btn btn-secondary btn-action">Назад</a>
</div>
</div>
</div>
<form method="post">
{% csrf_token %}
<!-- Основная информация -->
<div class="form-section">
<div class="form-section-header">
<h4>Основная информация</h4>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.name.id_for_label }}" class="form-label">Имя объекта:</label>
{{ form.name }}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Дата создания:</label>
<div class="readonly-field">
{% if object.created_at %}{{ object.created_at|date:"d.m.Y H:i" }}{% else %}-{% endif %}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Создан пользователем:</label>
<div class="readonly-field">
{% if object.created_by %}{{ object.created_by }}{% else %}-{% endif %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Дата последнего изменения:</label>
<div class="readonly-field">
{% if object.updated_at %}{{ object.updated_at|date:"d.m.Y H:i" }}{% else %}-{% endif %}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Изменен пользователем:</label>
<div class="readonly-field">
{% if object.updated_by %}{{ object.updated_by }}{% else %}-{% endif %}
</div>
</div>
</div>
</div>
</div>
<!-- ВЧ загрузки -->
<div class="form-section">
<div class="form-section-header d-flex justify-content-between align-items-center">
<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 %}
</div>
<div id="parameters-container">
{% for param_form in parameter_forms %}
<div class="dynamic-form" data-parameter-index="{{ forloop.counter0 }}">
<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 %}
</div>
<div class="row">
<div class="col-md-3">
<div class="mb-3">
<label for="{{ param_form.id_satellite.id_for_label }}" class="form-label">Спутник:</label>
{{ param_form.id_satellite }}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ param_form.frequency.id_for_label }}" class="form-label">Частота, МГц:</label>
{{ param_form.frequency }}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ param_form.freq_range.id_for_label }}" class="form-label">Полоса, МГц:</label>
{{ param_form.freq_range }}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ param_form.polarization.id_for_label }}" class="form-label">Поляризация:</label>
{{ param_form.polarization }}
</div>
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="mb-3">
<label for="{{ param_form.bod_velocity.id_for_label }}" class="form-label">Симв. скорость, БОД:</label>
{{ param_form.bod_velocity }}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ param_form.modulation.id_for_label }}" class="form-label">Модуляция:</label>
{{ param_form.modulation }}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ param_form.snr.id_for_label }}" class="form-label">ОСШ:</label>
{{ param_form.snr }}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ param_form.standard.id_for_label }}" class="form-label">Стандарт:</label>
{{ param_form.standard }}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<!-- Блок с картой -->
<div class="form-section">
<div class="form-section-header">
<h4>Карта</h4>
</div>
<div class="map-container">
<div id="map"></div>
</div>
</div>
<!-- Геоданные -->
<div class="form-section">
<div class="form-section-header">
<h4>Геоданные</h4>
</div>
<!-- Координаты геолокации -->
<div class="coord-sync-group">
<div class="coord-group-header">Координаты геолокации</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="id_geo_latitude" class="form-label">Широта:</label>
<input type="number" step="0.000001" class="form-control"
id="id_geo_latitude" name="geo_latitude"
value="{% if object.geo_obj and object.geo_obj.coords %}{{ object.geo_obj.coords.y }}{% endif %}">
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="id_geo_longitude" class="form-label">Долгота:</label>
<input type="number" step="0.000001" class="form-control"
id="id_geo_longitude" name="geo_longitude"
value="{% if object.geo_obj and object.geo_obj.coords %}{{ object.geo_obj.coords.x }}{% endif %}">
</div>
</div>
</div>
</div>
<!-- Координаты Кубсата -->
<div class="coord-group">
<div class="coord-group-header">Координаты Кубсата</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="id_kupsat_longitude" class="form-label">Долгота:</label>
<input type="number" step="0.000001" class="form-control"
id="id_kupsat_longitude" name="kupsat_longitude"
value="{% if object.geo_obj and object.geo_obj.coords_kupsat %}{{ object.geo_obj.coords_kupsat.x }}{% endif %}">
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="id_kupsat_latitude" class="form-label">Широта:</label>
<input type="number" step="0.000001" class="form-control"
id="id_kupsat_latitude" name="kupsat_latitude"
value="{% if object.geo_obj and object.geo_obj.coords_kupsat %}{{ object.geo_obj.coords_kupsat.y }}{% endif %}">
</div>
</div>
</div>
</div>
<!-- Координаты оперативников -->
<div class="coord-group">
<div class="coord-group-header">Координаты оперативников</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="id_valid_longitude" class="form-label">Долгота:</label>
<input type="number" step="0.000001" class="form-control"
id="id_valid_longitude" name="valid_longitude"
value="{% if object.geo_obj and object.geo_obj.coords_valid %}{{ object.geo_obj.coords_valid.x }}{% endif %}">
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="id_valid_latitude" class="form-label">Широта:</label>
<input type="number" step="0.000001" class="form-control"
id="id_valid_latitude" name="valid_latitude"
value="{% if object.geo_obj and object.geo_obj.coords_valid %}{{ object.geo_obj.coords_valid.y }}{% endif %}">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ geo_form.location.id_for_label }}" class="form-label">Местоположение:</label>
{{ geo_form.location }}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ geo_form.comment.id_for_label }}" class="form-label">Комментарий:</label>
{{ geo_form.comment }}
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Дата и время:</label>
<div class="datetime-group">
<div>
<label for="id_timestamp_date" class="form-label">Дата:</label>
<input type="date" class="form-control"
id="id_timestamp_date" name="timestamp_date"
value="{% if object.geo_obj and object.geo_obj.timestamp %}{{ object.geo_obj.timestamp|date:'Y-m-d' }}{% endif %}">
</div>
<div>
<label for="id_timestamp_time" class="form-label">Время:</label>
<input type="time" class="form-control"
id="id_timestamp_time" name="timestamp_time"
value="{% if object.geo_obj and object.geo_obj.timestamp %}{{ object.geo_obj.timestamp|time:'H:i' }}{% endif %}">
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ geo_form.is_average.id_for_label }}" class="form-check-label">Усреднённое:</label>
{{ geo_form.is_average }}
</div>
</div>
</div>
{% if object.geo_obj %}
<div class="row mt-3">
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">Расстояние гео-кубсат, км:</label>
<div class="readonly-field">
{% if object.geo_obj.distance_coords_kup is not None %}
{{ object.geo_obj.distance_coords_kup|floatformat:2 }}
{% else %}
-
{% endif %}
</div>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">Расстояние гео-опер, км:</label>
<div class="readonly-field">
{% if object.geo_obj.distance_coords_valid is not None %}
{{ object.geo_obj.distance_coords_valid|floatformat:2 }}
{% else %}
-
{% endif %}
</div>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">Расстояние кубсат-опер, км:</label>
<div class="readonly-field">
{% if object.geo_obj.distance_kup_valid is not None %}
{{ object.geo_obj.distance_kup_valid|floatformat:2 }}
{% else %}
-
{% endif %}
</div>
</div>
</div>
</div>
{% endif %}
</div>
<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>
</form>
</div>
{% endblock %}
{% block extra_js %}
{{ block.super }}
<!-- Подключаем Leaflet и его плагины -->
{% leaflet_js %}
{% leaflet_css %}
<script>
// Динамическое добавление ВЧ загрузок
let parameterIndex = {{ parameter_forms|length }};
document.getElementById('add-parameter')?.addEventListener('click', function() {
const container = document.getElementById('parameters-container');
const template = document.querySelector('.dynamic-form');
if (template) {
const clone = template.cloneNode(true);
clone.querySelectorAll('[id]').forEach(el => {
el.id = el.id.replace(/-\d+-/g, `-${parameterIndex}-`);
});
clone.querySelectorAll('[name]').forEach(el => {
el.name = el.name.replace(/-\d+-/g, `-${parameterIndex}-`);
});
clone.querySelectorAll('[for]').forEach(el => {
el.htmlFor = el.htmlFor.replace(/-\d+-/g, `-${parameterIndex}-`);
});
clone.querySelector('.dynamic-form-header h5').textContent = `ВЧ загрузка #${parameterIndex + 1}`;
clone.dataset.parameterIndex = parameterIndex;
container.appendChild(clone);
parameterIndex++;
}
});
// Удаление ВЧ загрузок
document.addEventListener('click', function(e) {
if (e.target.classList.contains('remove-parameter')) {
if (document.querySelectorAll('.dynamic-form').length > 1) {
e.target.closest('.dynamic-form').remove();
} else {
alert('Должна быть хотя бы одна ВЧ загрузка');
}
}
});
document.addEventListener('DOMContentLoaded', function() {
// Инициализация карты
const map = L.map('map').setView([55.75, 37.62], 5);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
// Маркеры для разных типов координат
const markers = {
geo: L.marker([55.75, 37.62], { draggable: true, color: 'blue' }).addTo(map),
kupsat: L.marker([55.75, 37.61], { draggable: true, color: 'red' }).addTo(map),
valid: L.marker([55.75, 37.63], { draggable: true, color: 'green' }).addTo(map)
};
// Устанавливаем начальные координаты из полей формы
function initMarkersFromForm() {
const geoLat = parseFloat(document.getElementById('id_geo_latitude').value) || 55.75;
const geoLng = parseFloat(document.getElementById('id_geo_longitude').value) || 37.62;
markers.geo.setLatLng([geoLat, geoLng]);
const kupsatLat = parseFloat(document.getElementById('id_kupsat_latitude').value) || 55.75;
const kupsatLng = parseFloat(document.getElementById('id_kupsat_longitude').value) || 37.61;
markers.kupsat.setLatLng([kupsatLat, kupsatLng]);
const validLat = parseFloat(document.getElementById('id_valid_latitude').value) || 55.75;
const validLng = parseFloat(document.getElementById('id_valid_longitude').value) || 37.63;
markers.valid.setLatLng([validLat, validLng]);
// Центрируем карту на первом маркере
map.setView(markers.geo.getLatLng(), 10);
}
// Обновляем координаты в форме при перетаскивании маркеров
function setupMarkerDrag(marker, latFieldId, lngFieldId, color) {
marker.on('dragend', function(event) {
const latLng = event.target.getLatLng();
document.getElementById(latFieldId).value = latLng.lat.toFixed(6);
document.getElementById(lngFieldId).value = latLng.lng.toFixed(6);
});
}
// Обновляем положение маркера при изменении координат в форме
function setupFormChange(latFieldId, lngFieldId, marker) {
const latField = document.getElementById(latFieldId);
const lngField = document.getElementById(lngFieldId);
[latField, lngField].forEach(field => {
field.addEventListener('change', function() {
const lat = parseFloat(latField.value);
const lng = parseFloat(lngField.value);
if (!isNaN(lat) && !isNaN(lng)) {
marker.setLatLng([lat, lng]);
map.setView(marker.getLatLng(), 10);
}
});
});
}
// Инициализация
initMarkersFromForm();
// Настройка маркеров
setupMarkerDrag(markers.geo, 'id_geo_latitude', 'id_geo_longitude', 'blue');
setupMarkerDrag(markers.kupsat, 'id_kupsat_latitude', 'id_kupsat_longitude', 'red');
setupMarkerDrag(markers.valid, 'id_valid_latitude', 'id_valid_longitude', 'green');
// Настройка формы
setupFormChange('id_geo_latitude', 'id_geo_longitude', markers.geo);
setupFormChange('id_kupsat_latitude', 'id_kupsat_longitude', markers.kupsat);
setupFormChange('id_valid_latitude', 'id_valid_longitude', markers.valid);
// Легенда
const legend = L.control({ position: 'bottomright' });
legend.onAdd = function() {
const div = L.DomUtil.create('div', 'info legend');
div.innerHTML = `
<h6>Легенда</h6>
<div><span style="color: blue; font-weight: bold;">•</span> Геолокация</div>
<div><span style="color: red; font-weight: bold;">•</span> Кубсат</div>
<div><span style="color: green; font-weight: bold;">•</span> Оперативники</div>
`;
return div;
};
legend.addTo(map);
});
</script>
{% endblock %}

View File

@@ -50,6 +50,116 @@
</select>
</div>
<!-- Column visibility toggle button -->
<div class="dropdown">
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" id="columnVisibilityDropdown" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-gear"></i> Колонки
</button>
<ul class="dropdown-menu" aria-labelledby="columnVisibilityDropdown" style="z-index: 1050;">
<li>
<label class="dropdown-item">
<input type="checkbox" id="select-all-columns" checked onchange="toggleAllColumns(this)"> Выбрать всё
</label>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<label class="dropdown-item">
<input type="checkbox" class="column-toggle" data-column="0" checked onchange="toggleColumn(this)"> Выбрать
</label>
</li>
<li>
<label class="dropdown-item">
<input type="checkbox" class="column-toggle" data-column="1" checked onchange="toggleColumn(this)"> Имя
</label>
</li>
<li>
<label class="dropdown-item">
<input type="checkbox" class="column-toggle" data-column="2" checked onchange="toggleColumn(this)"> Спутник
</label>
</li>
<li>
<label class="dropdown-item">
<input type="checkbox" class="column-toggle" data-column="3" checked onchange="toggleColumn(this)"> Част, МГц
</label>
</li>
<li>
<label class="dropdown-item">
<input type="checkbox" class="column-toggle" data-column="4" checked onchange="toggleColumn(this)"> Полоса, МГц
</label>
</li>
<li>
<label class="dropdown-item">
<input type="checkbox" class="column-toggle" data-column="5" checked onchange="toggleColumn(this)"> Поляризация
</label>
</li>
<li>
<label class="dropdown-item">
<input type="checkbox" class="column-toggle" data-column="6" checked onchange="toggleColumn(this)"> Сим. V
</label>
</li>
<li>
<label class="dropdown-item">
<input type="checkbox" class="column-toggle" data-column="7" checked onchange="toggleColumn(this)"> Модул
</label>
</li>
<li>
<label class="dropdown-item">
<input type="checkbox" class="column-toggle" data-column="8" checked onchange="toggleColumn(this)"> ОСШ
</label>
</li>
<li>
<label class="dropdown-item">
<input type="checkbox" class="column-toggle" data-column="9" checked onchange="toggleColumn(this)"> Геолокация
</label>
</li>
<li>
<label class="dropdown-item">
<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)"> Опер. отд
</label>
</li>
<li>
<label class="dropdown-item">
<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)"> Гео-опер, км
</label>
</li>
<li>
<label class="dropdown-item">
<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)"> Обновлено
</label>
</li>
<li>
<label class="dropdown-item">
<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)"> Создано
</label>
</li>
<li>
<label class="dropdown-item">
<input type="checkbox" class="column-toggle" data-column="18" checked onchange="toggleColumn(this)"> Кем (создание)
</label>
</li>
</ul>
</div>
<!-- Pagination moved here -->
<div class="ms-auto">
{% if page_obj.paginator.num_pages > 1 %}
@@ -260,6 +370,8 @@
<th scope="col">Куб-опер, км</th>
<th scope="col">Обновлено</th>
<th scope="col">Кем</th>
<th scope="col">Создано</th>
<th scope="col">Кем (создание)</th>
</tr>
</thead>
<tbody>
@@ -268,7 +380,7 @@
<td class="text-center">
<input type="checkbox" class="form-check-input item-checkbox" value="{{ item.id }}">
</td>
<td>{{ item.name }}</td>
<td><a href="{% if item.obj.id %}{% url 'objitem_update' item.obj.id %}{% endif %}">{{ item.name }}</a></td>
<td>{{ item.satellite_name }}</td>
<td>{{ item.frequency }}</td>
<td>{{ item.freq_range }}</td>
@@ -283,11 +395,13 @@
<td>{{ item.distance_geo_valid }}</td>
<td>{{ item.distance_kup_valid }}</td>
<td>{{ item.obj.updated_at|date:"d.m.Y H:i" }}</td>
<td>{{ item.obj.updated_by }}</td>
<td>{{ item.updated_by }}</td>
<td>{{ item.obj.created_at|date:"d.m.Y H:i" }}</td>
<td>{{ item.obj.created_by }}</td>
</tr>
{% empty %}
<tr>
<td colspan="17" class="text-center py-4">
<td colspan="19" class="text-center py-4">
{% if selected_satellite_id %}
Нет данных для выбранных фильтров
{% else %}
@@ -307,6 +421,30 @@
<!-- JavaScript for checkbox functionality and filters -->
<script>
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 = '';
});
} else {
cells.forEach(cell => {
cell.style.display = 'none';
});
}
}
function toggleAllColumns(selectAllCheckbox) {
const columnCheckboxes = document.querySelectorAll('.column-toggle');
columnCheckboxes.forEach(checkbox => {
checkbox.checked = selectAllCheckbox.checked;
toggleColumn(checkbox);
});
}
document.addEventListener('DOMContentLoaded', function() {
// Select/Deselect all checkboxes
const selectAllCheckbox = document.getElementById('select-all');
@@ -451,6 +589,25 @@ document.addEventListener('DOMContentLoaded', function() {
// 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="17"]');
const creationUserCheckbox = document.querySelector('input[data-column="18"]');
if (creationDateCheckbox) {
creationDateCheckbox.checked = false; // Uncheck to hide by default
toggleColumn(creationDateCheckbox);
}
if (creationUserCheckbox) {
creationUserCheckbox.checked = false; // Uncheck to hide by default
toggleColumn(creationUserCheckbox);
}
}
// Initialize column visibility after page loads
setTimeout(initColumnVisibility, 100); // Slight delay to ensure DOM is fully loaded
});
</script>
{% endblock %}

View File

@@ -18,6 +18,9 @@ urlpatterns = [
path('vch-upload/', views.UploadVchLoadView.as_view(), name='vch_load'),
path('vch-link/', views.LinkVchSigmaView.as_view(), name='link_vch_sigma'),
path('kubsat-excel/', views.ProcessKubsatView.as_view(), name='kubsat_excel'),
path('object/create/', views.ObjItemCreateView.as_view(), name='objitem_create'),
path('object/<int:pk>/edit/', views.ObjItemUpdateView.as_view(), name='objitem_update'),
path('object/<int:pk>/delete/', views.ObjItemDeleteView.as_view(), name='objitem_delete'),
# path('upload/', views.upload_file, name='upload_file'),
]

View File

@@ -52,7 +52,7 @@ def remove_str(s: str):
return float(s.strip().replace(",", "."))
return s
def fill_data_from_df(df: pd.DataFrame, sat: Satellite):
def fill_data_from_df(df: pd.DataFrame, sat: Satellite, current_user=None):
try:
df.rename(columns={'Модуляция ': 'Модуляция'}, inplace=True)
except Exception as e:
@@ -111,6 +111,7 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite):
location = stroka[1]['Местоопределение'].strip()
comment = stroka[1]['Комментарий']
source = stroka[1]['Объект наблюдения']
user_to_use = current_user if current_user else CustomUser.objects.get(id=1)
vch_load_obj, _ = Parameter.objects.get_or_create(
id_satellite=sat,
@@ -120,7 +121,6 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite):
bod_velocity=v,
modulation=mod_obj,
snr=snr,
# defaults={'id_user_add': CustomUser.objects.get(id=1)}
)
geo, _ = Geo.objects.get_or_create(
@@ -131,7 +131,7 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite):
'coords_valid': valid_point,
'location': location,
'comment': comment,
'is_average': (comment != -1.0)
'is_average': (comment != -1.0),
}
)
geo.save()
@@ -144,7 +144,7 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite):
if not existing_obj_items.exists():
obj_item = ObjItem.objects.create(
name=source,
id_user_add=CustomUser.objects.get(id=1)
created_by=user_to_use
)
obj_item.parameters_obj.set([vch_load_obj])
geo.objitem = obj_item
@@ -202,7 +202,7 @@ def get_point_from_json(filepath: str):
def get_points_from_csv(file_content):
def get_points_from_csv(file_content, current_user=None):
df = pd.read_csv(io.StringIO(file_content), sep=";",
names=['id', 'obj', 'lat', 'lon', 'h', 'time', 'sat', 'norad_id', 'freq', 'f_range', 'et', 'qaul', 'mir_1', 'mir_2', 'mir_3'])
df[['lat', 'lon', 'freq', 'f_range']] = df[['lat', 'lon', 'freq', 'f_range']].replace(',', '.', regex=True).astype(float)
@@ -239,12 +239,14 @@ def get_points_from_csv(file_content):
name=row['mir_3']
)
user_to_use = current_user if current_user else CustomUser.objects.get(id=1)
vch_load_obj, _ = Parameter.objects.get_or_create(
id_satellite=sat_obj,
polarization=pol_obj,
frequency=row['freq'],
freq_range=row['f_range'],
# defaults={'id_user_add': CustomUser.objects.get(id=1)}
# defaults={'id_user_add': user_to_use}
)
geo_obj, _ = Geo.objects.get_or_create(
@@ -252,7 +254,7 @@ def get_points_from_csv(file_content):
coords=Point(row['lon'], row['lat'], srid=4326),
defaults={
'is_average': False,
# 'id_user_add': CustomUser.objects.get(id=1),
# 'id_user_add': user_to_use,
}
)
geo_obj.mirrors.set(Mirror.objects.filter(name__in=mir_lst))
@@ -264,7 +266,7 @@ def get_points_from_csv(file_content):
if not existing_obj_items.exists():
obj_item = ObjItem.objects.create(
name=row['obj'],
# id_user_add=CustomUser.objects.get(id=1)
created_by=user_to_use
)
obj_item.parameters_obj.set([vch_load_obj])
geo_obj.objitem = obj_item

View File

@@ -6,10 +6,13 @@ from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views import View
from django.views.generic import TemplateView, FormView
from django.views.generic import TemplateView, FormView, UpdateView, DeleteView, CreateView
from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
from django.contrib.auth import logout
from django.forms import inlineformset_factory, modelformset_factory
from django.db import models
from django.urls import reverse_lazy
from django.contrib.gis.geos import Point
import pandas as pd
from .utils import (
fill_data_from_df,
@@ -20,10 +23,21 @@ from .utils import (
kub_report
)
from mapsapp.utils import parse_transponders_from_json, parse_transponders_from_xml
from .forms import LoadExcelData, LoadCsvData, UploadFileForm, VchLinkForm, UploadVchLoad, NewEventForm
from .forms import (
LoadExcelData,
LoadCsvData,
UploadFileForm,
VchLinkForm,
UploadVchLoad,
NewEventForm,
ObjItemForm,
ParameterForm,
GeoForm
)
from .models import ObjItem
from .clusters import get_clusters
from io import BytesIO
from datetime import datetime
@@ -93,7 +107,7 @@ class LoadExcelDataView(LoginRequiredMixin, FormView):
df = pd.read_excel(io.BytesIO(uploaded_file.read()))
if number > 0:
df = df.head(number)
result = fill_data_from_df(df, selected_sat)
result = fill_data_from_df(df, selected_sat, self.request.user.customuser)
messages.success(self.request, f"Данные успешно загружены! Обработано строк: {result}")
return redirect('load_excel_data')
@@ -151,7 +165,7 @@ class LoadCsvDataView(LoginRequiredMixin, FormView):
if isinstance(content, bytes):
content = content.decode('utf-8')
get_points_from_csv(content)
get_points_from_csv(content, self.request.user.customuser)
messages.success(self.request, f"Данные успешно загружены!")
return redirect('load_csv_data')
except Exception as e:
@@ -331,7 +345,7 @@ class ObjItemListView(LoginRequiredMixin, View):
try:
items_per_page = int(items_per_page)
except ValueError:
items_per_page = 25
items_per_page = 50
# Only filter objects by selected satellite if provided (no data shown by default)
objects = ObjItem.objects.none() # Initially empty
@@ -348,11 +362,11 @@ class ObjItemListView(LoginRequiredMixin, View):
# Start with the basic filter if any satellites are selected
if selected_satellites:
# Start with the basic filter
# Start with the basic filter - optimized with prefetch_related for all related objects
objects = ObjItem.objects.select_related(
'id_user_add__user',
'geo_obj',
'updated_by__user'
'updated_by__user',
'created_by__user',
).prefetch_related(
'parameters_obj__id_satellite',
'parameters_obj__polarization',
@@ -362,9 +376,9 @@ class ObjItemListView(LoginRequiredMixin, View):
else:
# If no satellites are selected, start with all objects
objects = ObjItem.objects.select_related(
'id_user_add__user',
'geo_obj',
'updated_by__user'
'updated_by__user',
'created_by__user',
).prefetch_related(
'parameters_obj__id_satellite',
'parameters_obj__polarization',
@@ -468,8 +482,13 @@ class ObjItemListView(LoginRequiredMixin, View):
# Prepare the data to include calculated fields for the template
processed_objects = []
for obj in page_obj:
# Get the first parameter
param = obj.parameters_obj.first() if obj.parameters_obj.exists() else None
# Get the first parameter using the prefetched relation to avoid additional queries
param = None
if hasattr(obj, 'parameters_obj') and obj.parameters_obj.all():
# Get parameters from the prefetched queryset without triggering new query
param_list = list(obj.parameters_obj.all())
if param_list:
param = param_list[0] # Get first parameter without additional query
# Process geo coordinates
geo_coords = "-"
@@ -518,22 +537,51 @@ class ObjItemListView(LoginRequiredMixin, View):
if obj.geo_obj.distance_kup_valid is not None:
distance_kup_valid = f"{obj.geo_obj.distance_kup_valid:.3f}"
# Extract related object data to avoid additional queries in template
satellite_name = "-"
frequency = "-"
freq_range = "-"
polarization_name = "-"
bod_velocity = "-"
modulation_name = "-"
snr = "-"
if param:
# Get satellite data directly to avoid additional query
if hasattr(param, 'id_satellite') and param.id_satellite:
satellite_name = param.id_satellite.name if hasattr(param.id_satellite, 'name') else "-"
# Get parameter values directly
frequency = f"{param.frequency:.3f}" if param.frequency is not None else "-"
freq_range = f"{param.freq_range:.3f}" if param.freq_range is not None else "-"
bod_velocity = f"{param.bod_velocity:.0f}" if param.bod_velocity is not None else "-"
snr = f"{param.snr:.0f}" if param.snr is not None else "-"
# Get polarization name directly to avoid additional query
if hasattr(param, 'polarization') and param.polarization:
polarization_name = param.polarization.name if hasattr(param.polarization, 'name') else "-"
# Get modulation name directly to avoid additional query
if hasattr(param, 'modulation') and param.modulation:
modulation_name = param.modulation.name if hasattr(param.modulation, 'name') else "-"
processed_objects.append({
'id': obj.id,
'name': obj.name or "-",
'satellite_name': param.id_satellite.name if param and param.id_satellite else "-",
'frequency': f"{param.frequency:.3f}" if param and param.frequency else "-",
'freq_range': f"{param.freq_range:.3f}" if param and param.freq_range else "-",
'polarization': param.polarization.name if param and param.polarization else "-",
'bod_velocity': f"{param.bod_velocity:.0f}" if param and param.bod_velocity else "-",
'modulation': param.modulation.name if param and param.modulation else "-",
'snr': f"{param.snr:.0f}" if param and param.snr else "-",
'satellite_name': satellite_name,
'frequency': frequency,
'freq_range': freq_range,
'polarization': polarization_name,
'bod_velocity': bod_velocity,
'modulation': modulation_name,
'snr': snr,
'geo_coords': geo_coords,
'kupsat_coords': kupsat_coords,
'valid_coords': valid_coords,
'distance_geo_kup': distance_geo_kup,
'distance_geo_valid': distance_geo_valid,
'distance_kup_valid': distance_kup_valid,
'updated_by': obj.updated_by if obj.updated_by else '-',
'obj': obj
})
@@ -572,3 +620,208 @@ class ObjItemListView(LoginRequiredMixin, View):
}
return render(request, 'mainapp/objitem_list.html', context)
class ObjItemUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = ObjItem
form_class = ObjItemForm
template_name = 'mainapp/objitem_form.html'
success_url = reverse_lazy('home')
def test_func(self):
return self.request.user.customuser.role in ['admin', 'moderator']
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Добавляем контекст для карты
context['LEAFLET_CONFIG'] = {
'DEFAULT_CENTER': (55.75, 37.62),
'DEFAULT_ZOOM': 5,
}
# Остальной контекст остается без изменений
ParameterFormSet = modelformset_factory(
Parameter,
form=ParameterForm,
extra=0,
can_delete=True
)
if self.object:
parameter_queryset = self.object.parameters_obj.all()
context['parameter_forms'] = ParameterFormSet(
queryset=parameter_queryset,
prefix='parameters'
)
if hasattr(self.object, 'geo_obj'):
context['geo_form'] = GeoForm(instance=self.object.geo_obj, prefix='geo')
else:
context['geo_form'] = GeoForm(prefix='geo')
else:
context['parameter_forms'] = ParameterFormSet(
queryset=Parameter.objects.none(),
prefix='parameters'
)
context['geo_form'] = GeoForm(prefix='geo')
return context
def form_valid(self, form):
context = self.get_context_data()
parameter_forms = context['parameter_forms']
geo_form = context['geo_form']
# Сохраняем основной объект
self.object = form.save(commit=False)
self.object.updated_by = self.request.user.customuser
self.object.save()
# Сохраняем связанные параметры
if parameter_forms.is_valid():
instances = parameter_forms.save(commit=False)
for instance in instances:
instance.save()
instance.objitems.set([self.object])
# Сохраняем геоданные
geo_instance = None
if hasattr(self.object, 'geo_obj'):
geo_instance = self.object.geo_obj
# Создаем или обновляем гео-объект
if geo_instance is None:
geo_instance = Geo(objitem=self.object)
# Обновляем поля из geo_form
if geo_form.is_valid():
geo_instance.location = geo_form.cleaned_data['location']
geo_instance.comment = geo_form.cleaned_data['comment']
geo_instance.is_average = geo_form.cleaned_data['is_average']
# Обрабатываем координаты геолокации
geo_longitude = self.request.POST.get('geo_longitude')
geo_latitude = self.request.POST.get('geo_latitude')
if geo_longitude and geo_latitude:
geo_instance.coords = Point(float(geo_longitude), float(geo_latitude))
# Обрабатываем координаты Кубсата
kupsat_longitude = self.request.POST.get('kupsat_longitude')
kupsat_latitude = self.request.POST.get('kupsat_latitude')
if kupsat_longitude and kupsat_latitude:
geo_instance.coords_kupsat = Point(float(kupsat_longitude), float(kupsat_latitude))
# Обрабатываем координаты оперативников
valid_longitude = self.request.POST.get('valid_longitude')
valid_latitude = self.request.POST.get('valid_latitude')
if valid_longitude and valid_latitude:
geo_instance.coords_valid = Point(float(valid_longitude), float(valid_latitude))
# Обрабатываем дату/время
timestamp_date = self.request.POST.get('timestamp_date')
timestamp_time = self.request.POST.get('timestamp_time')
if timestamp_date and timestamp_time:
naive_datetime = datetime.strptime(f"{timestamp_date} {timestamp_time}", "%Y-%m-%d %H:%M")
geo_instance.timestamp = naive_datetime
geo_instance.save()
messages.success(self.request, 'Объект успешно сохранён!')
return super().form_valid(form)
class ObjItemCreateView(LoginRequiredMixin, UserPassesTestMixin, CreateView):
model = ObjItem
form_class = ObjItemForm
template_name = 'mainapp/objitem_form.html'
success_url = reverse_lazy('home')
def test_func(self):
return self.request.user.customuser.role in ['admin', 'moderator']
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
ParameterFormSet = modelformset_factory(
Parameter,
form=ParameterForm,
extra=1,
can_delete=True
)
context['parameter_forms'] = ParameterFormSet(
queryset=Parameter.objects.none(),
prefix='parameters'
)
context['geo_form'] = GeoForm(prefix='geo')
return context
def form_valid(self, form):
context = self.get_context_data()
parameter_forms = context['parameter_forms']
geo_form = context['geo_form']
# Сохраняем основной объект
self.object = form.save(commit=False)
self.object.created_by = self.request.user.customuser
self.object.updated_by = self.request.user.customuser
self.object.save()
# Сохраняем связанные параметры
if parameter_forms.is_valid():
instances = parameter_forms.save(commit=False)
for instance in instances:
instance.save()
instance.objitems.add(self.object)
# Создаем гео-объект
geo_instance = Geo(objitem=self.object)
# Обновляем поля из geo_form
if geo_form.is_valid():
geo_instance.location = geo_form.cleaned_data['location']
geo_instance.comment = geo_form.cleaned_data['comment']
geo_instance.is_average = geo_form.cleaned_data['is_average']
# Обрабатываем координаты геолокации
geo_longitude = self.request.POST.get('geo_longitude')
geo_latitude = self.request.POST.get('geo_latitude')
if geo_longitude and geo_latitude:
geo_instance.coords = Point(float(geo_longitude), float(geo_latitude))
# Обрабатываем координаты Кубсата
kupsat_longitude = self.request.POST.get('kupsat_longitude')
kupsat_latitude = self.request.POST.get('kupsat_latitude')
if kupsat_longitude and kupsat_latitude:
geo_instance.coords_kupsat = Point(float(kupsat_longitude), float(kupsat_latitude))
# Обрабатываем координаты оперативников
valid_longitude = self.request.POST.get('valid_longitude')
valid_latitude = self.request.POST.get('valid_latitude')
if valid_longitude and valid_latitude:
geo_instance.coords_valid = Point(float(valid_longitude), float(valid_latitude))
# Обрабатываем дату/время
timestamp_date = self.request.POST.get('timestamp_date')
timestamp_time = self.request.POST.get('timestamp_time')
if timestamp_date and timestamp_time:
naive_datetime = datetime.strptime(f"{timestamp_date} {timestamp_time}", "%Y-%m-%d %H:%M")
geo_instance.timestamp = naive_datetime
geo_instance.save()
messages.success(self.request, 'Объект успешно создан!')
return super().form_valid(form)
class ObjItemDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = ObjItem
template_name = 'mainapp/objitem_confirm_delete.html'
success_url = reverse_lazy('home')
def test_func(self):
return self.request.user.customuser.role in ['admin', 'moderator']
def delete(self, request, *args, **kwargs):
messages.success(self.request, 'Объект успешно удалён!')
return super().delete(request, *args, **kwargs)