Внёс мелкие правки и фиксы
This commit is contained in:
@@ -26,6 +26,7 @@ from .models import (
|
|||||||
SigmaParMark,
|
SigmaParMark,
|
||||||
ObjectMark,
|
ObjectMark,
|
||||||
ObjectInfo,
|
ObjectInfo,
|
||||||
|
ObjectOwnership,
|
||||||
SigmaParameter,
|
SigmaParameter,
|
||||||
Parameter,
|
Parameter,
|
||||||
Satellite,
|
Satellite,
|
||||||
@@ -404,6 +405,15 @@ class ObjectInfoAdmin(BaseAdmin):
|
|||||||
ordering = ("name",)
|
ordering = ("name",)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(ObjectOwnership)
|
||||||
|
class ObjectOwnershipAdmin(BaseAdmin):
|
||||||
|
"""Админ-панель для модели ObjectOwnership (Принадлежность объекта)."""
|
||||||
|
|
||||||
|
list_display = ("name",)
|
||||||
|
search_fields = ("name",)
|
||||||
|
ordering = ("name",)
|
||||||
|
|
||||||
|
|
||||||
class SigmaParameterInline(admin.StackedInline):
|
class SigmaParameterInline(admin.StackedInline):
|
||||||
model = SigmaParameter
|
model = SigmaParameter
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|||||||
@@ -463,15 +463,20 @@ class SourceForm(forms.ModelForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Source
|
model = Source
|
||||||
fields = ['info']
|
fields = ['info', 'ownership']
|
||||||
widgets = {
|
widgets = {
|
||||||
'info': forms.Select(attrs={
|
'info': forms.Select(attrs={
|
||||||
'class': 'form-select',
|
'class': 'form-select',
|
||||||
'id': 'id_info',
|
'id': 'id_info',
|
||||||
}),
|
}),
|
||||||
|
'ownership': forms.Select(attrs={
|
||||||
|
'class': 'form-select',
|
||||||
|
'id': 'id_ownership',
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
labels = {
|
labels = {
|
||||||
'info': 'Тип объекта',
|
'info': 'Тип объекта',
|
||||||
|
'ownership': 'Принадлежность объекта',
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@@ -607,9 +612,8 @@ class KubsatFilterForm(forms.Form):
|
|||||||
widget=forms.SelectMultiple(attrs={'class': 'form-select', 'size': '3'})
|
widget=forms.SelectMultiple(attrs={'class': 'form-select', 'size': '3'})
|
||||||
)
|
)
|
||||||
|
|
||||||
# Заглушка для принадлежности объекта
|
object_ownership = forms.ModelMultipleChoiceField(
|
||||||
object_ownership = forms.MultipleChoiceField(
|
queryset=None,
|
||||||
choices=[],
|
|
||||||
label='Принадлежность объекта',
|
label='Принадлежность объекта',
|
||||||
required=False,
|
required=False,
|
||||||
widget=forms.SelectMultiple(attrs={'class': 'form-select', 'size': '3'})
|
widget=forms.SelectMultiple(attrs={'class': 'form-select', 'size': '3'})
|
||||||
@@ -658,7 +662,7 @@ class KubsatFilterForm(forms.Form):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
from mainapp.models import Band, ObjectInfo, Satellite, ObjItem
|
from mainapp.models import Band, ObjectInfo, ObjectOwnership, Satellite, ObjItem
|
||||||
from django.db.models import Exists, OuterRef
|
from django.db.models import Exists, OuterRef
|
||||||
|
|
||||||
# Фильтруем спутники: только те, у которых есть источники с точками
|
# Фильтруем спутники: только те, у которых есть источники с точками
|
||||||
@@ -669,6 +673,7 @@ class KubsatFilterForm(forms.Form):
|
|||||||
self.fields['satellites'].queryset = satellites_with_sources
|
self.fields['satellites'].queryset = satellites_with_sources
|
||||||
self.fields['band'].queryset = Band.objects.all().order_by('name')
|
self.fields['band'].queryset = Band.objects.all().order_by('name')
|
||||||
self.fields['object_type'].queryset = ObjectInfo.objects.all().order_by('name')
|
self.fields['object_type'].queryset = ObjectInfo.objects.all().order_by('name')
|
||||||
|
self.fields['object_ownership'].queryset = ObjectOwnership.objects.all().order_by('name')
|
||||||
|
|
||||||
|
|
||||||
class TransponderForm(forms.ModelForm):
|
class TransponderForm(forms.ModelForm):
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-11-20 11:45
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mainapp', '0008_objectinfo_source_info'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ObjectOwnership',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(help_text='Принадлежность объекта (страна, организация и т.д.)', max_length=255, unique=True, verbose_name='Принадлежность')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Принадлежность объекта',
|
||||||
|
'verbose_name_plural': 'Принадлежности объектов',
|
||||||
|
'ordering': ['name'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='source',
|
||||||
|
name='info',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='Тип объекта', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='source_info', to='mainapp.objectinfo', verbose_name='Тип объекта'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='source',
|
||||||
|
name='ownership',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='Принадлежность объекта (страна, организация и т.д.)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='source_ownership', to='mainapp.objectownership', verbose_name='Принадлежность объекта'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -84,6 +84,28 @@ class ObjectInfo(models.Model):
|
|||||||
ordering = ["name"]
|
ordering = ["name"]
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectOwnership(models.Model):
|
||||||
|
"""
|
||||||
|
Модель принадлежности объекта.
|
||||||
|
|
||||||
|
Определяет к какой организации/стране/группе принадлежит объект.
|
||||||
|
"""
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
unique=True,
|
||||||
|
verbose_name="Принадлежность",
|
||||||
|
help_text="Принадлежность объекта (страна, организация и т.д.)",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Принадлежность объекта"
|
||||||
|
verbose_name_plural = "Принадлежности объектов"
|
||||||
|
ordering = ["name"]
|
||||||
|
|
||||||
|
|
||||||
class ObjectMark(models.Model):
|
class ObjectMark(models.Model):
|
||||||
"""
|
"""
|
||||||
Модель отметки о наличии объекта.
|
Модель отметки о наличии объекта.
|
||||||
@@ -457,7 +479,17 @@ class Source(models.Model):
|
|||||||
related_name="source_info",
|
related_name="source_info",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name="Тип объекта"
|
verbose_name="Тип объекта",
|
||||||
|
help_text="Тип объекта",
|
||||||
|
)
|
||||||
|
ownership = models.ForeignKey(
|
||||||
|
'ObjectOwnership',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name="source_ownership",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
verbose_name="Принадлежность объекта",
|
||||||
|
help_text="Принадлежность объекта (страна, организация и т.д.)",
|
||||||
)
|
)
|
||||||
|
|
||||||
coords_average = gis.PointField(
|
coords_average = gis.PointField(
|
||||||
|
|||||||
@@ -23,10 +23,10 @@
|
|||||||
<a class="nav-link" href="{% url 'mainapp:objitem_list' %}">Точки</a>
|
<a class="nav-link" href="{% url 'mainapp:objitem_list' %}">Точки</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'mainapp:transponder_list' %}">Транспондеры</a>
|
<a class="nav-link" href="{% url 'mainapp:satellite_list' %}">Спутники</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'mainapp:satellite_list' %}">Спутники</a>
|
<a class="nav-link" href="{% url 'mainapp:transponder_list' %}">Транспондеры</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'lyngsatapp:lyngsat_list' %}">Справочные данные</a>
|
<a class="nav-link" href="{% url 'lyngsatapp:lyngsat_list' %}">Справочные данные</a>
|
||||||
|
|||||||
@@ -107,10 +107,17 @@
|
|||||||
<small class="form-text text-muted">Удерживайте Ctrl для выбора нескольких</small>
|
<small class="form-text text-muted">Удерживайте Ctrl для выбора нескольких</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Принадлежность объекта (заглушка) -->
|
<!-- Принадлежность объекта -->
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<label for="{{ form.object_ownership.id_for_label }}" class="form-label">{{ form.object_ownership.label }} (не работает)</label>
|
<label for="{{ form.object_ownership.id_for_label }}" class="form-label">{{ form.object_ownership.label }}</label>
|
||||||
|
<div class="d-flex justify-content-between mb-1">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||||
|
onclick="selectAllOptions('object_ownership', true)">Выбрать</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||||
|
onclick="selectAllOptions('object_ownership', false)">Снять</button>
|
||||||
|
</div>
|
||||||
{{ form.object_ownership }}
|
{{ form.object_ownership }}
|
||||||
|
<small class="form-text text-muted">Удерживайте Ctrl для выбора нескольких</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -231,6 +238,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th style="min-width: 80px;">ID объекта</th>
|
<th style="min-width: 80px;">ID объекта</th>
|
||||||
<th style="min-width: 120px;">Тип объекта</th>
|
<th style="min-width: 120px;">Тип объекта</th>
|
||||||
|
<th style="min-width: 150px;">Принадлежность объекта</th>
|
||||||
<th class="text-center" style="min-width: 100px;">Кол-во точек</th>
|
<th class="text-center" style="min-width: 100px;">Кол-во точек</th>
|
||||||
<th style="min-width: 120px;">Имя точки</th>
|
<th style="min-width: 120px;">Имя точки</th>
|
||||||
<th style="min-width: 150px;">Спутник</th>
|
<th style="min-width: 150px;">Спутник</th>
|
||||||
@@ -261,6 +269,24 @@
|
|||||||
<td rowspan="{{ source_data.objitems_data|length }}" class="source-type-cell">{{ source_data.source.info.name|default:"-" }}</td>
|
<td rowspan="{{ source_data.objitems_data|length }}" class="source-type-cell">{{ source_data.source.info.name|default:"-" }}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Принадлежность объекта (только для первой строки источника) -->
|
||||||
|
{% if forloop.first %}
|
||||||
|
<td rowspan="{{ source_data.objitems_data|length }}" class="source-ownership-cell">
|
||||||
|
{% if source_data.source.ownership %}
|
||||||
|
{% if source_data.source.ownership.name == "ТВ" and source_data.has_lyngsat %}
|
||||||
|
<a href="#" class="text-primary text-decoration-none"
|
||||||
|
onclick="showLyngsatModal({{ source_data.lyngsat_id }}); return false;">
|
||||||
|
<i class="bi bi-tv"></i> {{ source_data.source.ownership.name }}
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
{{ source_data.source.ownership.name }}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
-
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<!-- Количество точек (только для первой строки источника) -->
|
<!-- Количество точек (только для первой строки источника) -->
|
||||||
{% if forloop.first %}
|
{% if forloop.first %}
|
||||||
<td rowspan="{{ source_data.objitems_data|length }}" class="text-center source-count-cell" data-initial-count="{{ source_data.objitems_data|length }}">{{ source_data.objitems_data|length }}</td>
|
<td rowspan="{{ source_data.objitems_data|length }}" class="text-center source-count-cell" data-initial-count="{{ source_data.objitems_data|length }}">{{ source_data.objitems_data|length }}</td>
|
||||||
@@ -539,6 +565,55 @@ function selectAllOptions(selectName, selectAll) {
|
|||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
updateCounter();
|
updateCounter();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Функция для показа модального окна LyngSat
|
||||||
|
function showLyngsatModal(lyngsatId) {
|
||||||
|
const modal = new bootstrap.Modal(document.getElementById('lyngsatModal'));
|
||||||
|
modal.show();
|
||||||
|
|
||||||
|
const modalBody = document.getElementById('lyngsatModalBody');
|
||||||
|
modalBody.innerHTML = '<div class="text-center py-4"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Загрузка...</span></div></div>';
|
||||||
|
|
||||||
|
fetch('/api/lyngsat/' + lyngsatId + '/')
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Ошибка загрузки данных');
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
let html = '<div class="container-fluid"><div class="row g-3">' +
|
||||||
|
'<div class="col-md-6"><div class="card h-100">' +
|
||||||
|
'<div class="card-header bg-light"><strong><i class="bi bi-info-circle"></i> Основная информация</strong></div>' +
|
||||||
|
'<div class="card-body"><table class="table table-sm table-borderless mb-0"><tbody>' +
|
||||||
|
'<tr><td class="text-muted" style="width: 40%;">Спутник:</td><td><strong>' + data.satellite + '</strong></td></tr>' +
|
||||||
|
'<tr><td class="text-muted">Частота:</td><td><strong>' + data.frequency + ' МГц</strong></td></tr>' +
|
||||||
|
'<tr><td class="text-muted">Поляризация:</td><td><span class="badge bg-info">' + data.polarization + '</span></td></tr>' +
|
||||||
|
'<tr><td class="text-muted">Канал:</td><td>' + data.channel_info + '</td></tr>' +
|
||||||
|
'</tbody></table></div></div></div>' +
|
||||||
|
'<div class="col-md-6"><div class="card h-100">' +
|
||||||
|
'<div class="card-header bg-light"><strong><i class="bi bi-gear"></i> Технические параметры</strong></div>' +
|
||||||
|
'<div class="card-body"><table class="table table-sm table-borderless mb-0"><tbody>' +
|
||||||
|
'<tr><td class="text-muted" style="width: 40%;">Модуляция:</td><td><span class="badge bg-secondary">' + data.modulation + '</span></td></tr>' +
|
||||||
|
'<tr><td class="text-muted">Стандарт:</td><td><span class="badge bg-secondary">' + data.standard + '</span></td></tr>' +
|
||||||
|
'<tr><td class="text-muted">Сим. скорость:</td><td><strong>' + data.sym_velocity + ' БОД</strong></td></tr>' +
|
||||||
|
'<tr><td class="text-muted">FEC:</td><td>' + data.fec + '</td></tr>' +
|
||||||
|
'</tbody></table></div></div></div>' +
|
||||||
|
'<div class="col-12"><div class="card">' +
|
||||||
|
'<div class="card-header bg-light"><strong><i class="bi bi-clock-history"></i> Дополнительная информация</strong></div>' +
|
||||||
|
'<div class="card-body"><div class="row">' +
|
||||||
|
'<div class="col-md-6"><p class="mb-2"><span class="text-muted">Последнее обновление:</span><br><strong>' + data.last_update + '</strong></p></div>' +
|
||||||
|
'<div class="col-md-6">' + (data.url ? '<p class="mb-2"><span class="text-muted">Ссылка на объект:</span><br>' +
|
||||||
|
'<a href="' + data.url + '" target="_blank" class="btn btn-sm btn-outline-primary">' +
|
||||||
|
'<i class="bi bi-link-45deg"></i> Открыть на LyngSat</a></p>' : '') +
|
||||||
|
'</div></div></div></div></div></div></div>';
|
||||||
|
modalBody.innerHTML = html;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
modalBody.innerHTML = '<div class="alert alert-danger" role="alert">' +
|
||||||
|
'<i class="bi bi-exclamation-triangle"></i> ' + error.message + '</div>';
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -569,4 +644,29 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<!-- LyngSat Data Modal -->
|
||||||
|
<div class="modal fade" id="lyngsatModal" tabindex="-1" aria-labelledby="lyngsatModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header bg-primary text-white">
|
||||||
|
<h5 class="modal-title" id="lyngsatModalLabel">
|
||||||
|
<i class="bi bi-tv"></i> Данные объекта LyngSat
|
||||||
|
</h5>
|
||||||
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"
|
||||||
|
aria-label="Закрыть"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" id="lyngsatModalBody">
|
||||||
|
<div class="text-center py-4">
|
||||||
|
<div class="spinner-border text-primary" role="status">
|
||||||
|
<span class="visually-hidden">Загрузка...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -200,10 +200,10 @@
|
|||||||
|
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
<i class="bi bi-save"></i> Сохранить
|
Сохранить
|
||||||
</button>
|
</button>
|
||||||
<a href="{% url 'mainapp:satellite_list' %}" class="btn btn-secondary">
|
<a href="{% url 'mainapp:satellite_list' %}" class="btn btn-secondary">
|
||||||
<i class="bi bi-x-circle"></i> Отмена
|
Отмена
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -214,7 +214,7 @@
|
|||||||
|
|
||||||
{% if action == 'update' and transponders %}
|
{% if action == 'update' and transponders %}
|
||||||
<!-- Frequency Plan Visualization -->
|
<!-- Frequency Plan Visualization -->
|
||||||
<div class="row mt-4">
|
<!-- <div class="row mt-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -256,7 +256,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<div class="transponder-tooltip" id="transponderTooltip"></div>
|
<div class="transponder-tooltip" id="transponderTooltip"></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -206,6 +206,17 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="id_ownership" class="form-label">{{ form.ownership.label }}:</label>
|
||||||
|
{{ form.ownership }}
|
||||||
|
{% if form.ownership.errors %}
|
||||||
|
<div class="invalid-feedback d-block">
|
||||||
|
{{ form.ownership.errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -227,7 +238,7 @@
|
|||||||
|
|
||||||
<!-- Координаты ГЛ -->
|
<!-- Координаты ГЛ -->
|
||||||
<div class="coord-group">
|
<div class="coord-group">
|
||||||
<div class="coord-group-header">Координаты ГЛ (усреднённые)</div>
|
<div class="coord-group-header">Координаты ГЛ</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -581,9 +592,8 @@
|
|||||||
document.getElementById('save-btn').disabled = false;
|
document.getElementById('save-btn').disabled = false;
|
||||||
document.getElementById('cancel-btn').disabled = false;
|
document.getElementById('cancel-btn').disabled = false;
|
||||||
|
|
||||||
// Включаем drag для всех маркеров
|
Object.entries(markers).forEach(([key, m]) => {
|
||||||
Object.values(markers).forEach(m => {
|
if (key !== 'average' && m.marker.options.opacity !== 0) {
|
||||||
if (m.marker.options.opacity !== 0) {
|
|
||||||
m.marker.enableEditing();
|
m.marker.enableEditing();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -227,23 +227,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- LyngSat Filter -->
|
|
||||||
<div class="mb-2">
|
|
||||||
<label class="form-label">ТВ или нет:</label>
|
|
||||||
<div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" name="has_lyngsat" id="has_lyngsat_1"
|
|
||||||
value="1" {% if has_lyngsat == '1' %}checked{% endif %}>
|
|
||||||
<label class="form-check-label" for="has_lyngsat_1">Есть</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" name="has_lyngsat" id="has_lyngsat_0"
|
|
||||||
value="0" {% if has_lyngsat == '0' %}checked{% endif %}>
|
|
||||||
<label class="form-check-label" for="has_lyngsat_0">Нет</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ObjectInfo Filter -->
|
<!-- ObjectInfo Filter -->
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<label class="form-label">Тип объекта:</label>
|
<label class="form-label">Тип объекта:</label>
|
||||||
@@ -262,6 +245,24 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ObjectOwnership Filter -->
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label">Принадлежность объекта:</label>
|
||||||
|
<div class="d-flex justify-content-between mb-1">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||||
|
onclick="selectAllOptions('ownership_id', true)">Выбрать</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||||
|
onclick="selectAllOptions('ownership_id', false)">Снять</button>
|
||||||
|
</div>
|
||||||
|
<select name="ownership_id" class="form-select form-select-sm mb-2" multiple size="4">
|
||||||
|
{% for ownership in object_ownerships %}
|
||||||
|
<option value="{{ ownership.id }}" {% if ownership.id in selected_ownership %}selected{% endif %}>
|
||||||
|
{{ ownership.name }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Point Count Filter -->
|
<!-- Point Count Filter -->
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<label class="form-label">Количество точек:</label>
|
<label class="form-label">Количество точек:</label>
|
||||||
@@ -435,6 +436,7 @@
|
|||||||
<th scope="col" style="min-width: 150px;">Имя</th>
|
<th scope="col" style="min-width: 150px;">Имя</th>
|
||||||
<th scope="col" style="min-width: 120px;">Спутник</th>
|
<th scope="col" style="min-width: 120px;">Спутник</th>
|
||||||
<th scope="col" style="min-width: 120px;">Тип объекта</th>
|
<th scope="col" style="min-width: 120px;">Тип объекта</th>
|
||||||
|
<th scope="col" style="min-width: 150px;">Принадлежность объекта</th>
|
||||||
<th scope="col" style="min-width: 150px;">Координаты ГЛ</th>
|
<th scope="col" style="min-width: 150px;">Координаты ГЛ</th>
|
||||||
<th scope="col" class="text-center" style="min-width: 100px;">
|
<th scope="col" class="text-center" style="min-width: 100px;">
|
||||||
{% include 'mainapp/components/_sort_header.html' with field='objitem_count' label='Кол-во ГЛ(точек)' current_sort=sort %}
|
{% include 'mainapp/components/_sort_header.html' with field='objitem_count' label='Кол-во ГЛ(точек)' current_sort=sort %}
|
||||||
@@ -443,7 +445,6 @@
|
|||||||
<th scope="col" style="min-width: 150px;">Координаты визуального наблюдения</th>
|
<th scope="col" style="min-width: 150px;">Координаты визуального наблюдения</th>
|
||||||
<th scope="col" style="min-width: 150px;">Координаты справочные</th>
|
<th scope="col" style="min-width: 150px;">Координаты справочные</th>
|
||||||
<th scope="col" style="min-width: 180px;">Наличие сигнала</th>
|
<th scope="col" style="min-width: 180px;">Наличие сигнала</th>
|
||||||
<th scope="col" class="text-center" style="min-width: 80px;">ТВ или нет</th>
|
|
||||||
<th scope="col" style="min-width: 120px;">
|
<th scope="col" style="min-width: 120px;">
|
||||||
{% include 'mainapp/components/_sort_header.html' with field='created_at' label='Создано' current_sort=sort %}
|
{% include 'mainapp/components/_sort_header.html' with field='created_at' label='Создано' current_sort=sort %}
|
||||||
</th>
|
</th>
|
||||||
@@ -473,6 +474,16 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ source.info }}</td>
|
<td>{{ source.info }}</td>
|
||||||
|
<td>
|
||||||
|
{% if source.ownership == "ТВ" and source.has_lyngsat %}
|
||||||
|
<a href="#" class="text-primary text-decoration-none"
|
||||||
|
onclick="showLyngsatModal({{ source.lyngsat_id }}); return false;">
|
||||||
|
<i class="bi bi-tv"></i> {{ source.ownership }}
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
{{ source.ownership }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>{{ source.coords_average }}</td>
|
<td>{{ source.coords_average }}</td>
|
||||||
<td class="text-center">{{ source.objitem_count }}</td>
|
<td class="text-center">{{ source.objitem_count }}</td>
|
||||||
<td>{{ source.coords_kupsat }}</td>
|
<td>{{ source.coords_kupsat }}</td>
|
||||||
@@ -501,16 +512,6 @@
|
|||||||
<span class="text-muted">-</span>
|
<span class="text-muted">-</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
|
||||||
{% if source.has_lyngsat %}
|
|
||||||
<a href="#" class="text-primary text-decoration-none"
|
|
||||||
onclick="showLyngsatModal({{ source.lyngsat_id }}); return false;">
|
|
||||||
<i class="bi bi-tv"></i> ТВ
|
|
||||||
</a>
|
|
||||||
{% else %}
|
|
||||||
-
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>{{ source.created_at|date:"d.m.Y H:i" }}</td>
|
<td>{{ source.created_at|date:"d.m.Y H:i" }}</td>
|
||||||
<td>{{ source.updated_at|date:"d.m.Y H:i" }}</td>
|
<td>{{ source.updated_at|date:"d.m.Y H:i" }}</td>
|
||||||
@@ -1091,7 +1092,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
setupRadioLikeCheckboxes('has_coords_kupsat');
|
setupRadioLikeCheckboxes('has_coords_kupsat');
|
||||||
setupRadioLikeCheckboxes('has_coords_valid');
|
setupRadioLikeCheckboxes('has_coords_valid');
|
||||||
setupRadioLikeCheckboxes('has_coords_reference');
|
setupRadioLikeCheckboxes('has_coords_reference');
|
||||||
setupRadioLikeCheckboxes('has_lyngsat');
|
|
||||||
setupRadioLikeCheckboxes('has_signal_mark');
|
setupRadioLikeCheckboxes('has_signal_mark');
|
||||||
|
|
||||||
// Update filter counter on page load
|
// Update filter counter on page load
|
||||||
|
|||||||
@@ -570,3 +570,55 @@ class SatelliteDataAPIView(LoginRequiredMixin, View):
|
|||||||
return JsonResponse({'error': 'Спутник не найден'}, status=404)
|
return JsonResponse({'error': 'Спутник не найден'}, status=404)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return JsonResponse({'error': str(e)}, status=500)
|
return JsonResponse({'error': str(e)}, status=500)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SatelliteDataAPIView(LoginRequiredMixin, View):
|
||||||
|
"""API endpoint for getting Satellite data."""
|
||||||
|
|
||||||
|
def get(self, request, satellite_id):
|
||||||
|
from ..models import Satellite
|
||||||
|
|
||||||
|
try:
|
||||||
|
satellite = Satellite.objects.prefetch_related('band').get(id=satellite_id)
|
||||||
|
|
||||||
|
# Format launch_date
|
||||||
|
launch_date_str = '-'
|
||||||
|
if satellite.launch_date:
|
||||||
|
launch_date_str = satellite.launch_date.strftime("%d.%m.%Y")
|
||||||
|
|
||||||
|
# Format created_at and updated_at
|
||||||
|
created_at_str = '-'
|
||||||
|
if satellite.created_at:
|
||||||
|
local_time = timezone.localtime(satellite.created_at)
|
||||||
|
created_at_str = local_time.strftime("%d.%m.%Y %H:%M")
|
||||||
|
|
||||||
|
updated_at_str = '-'
|
||||||
|
if satellite.updated_at:
|
||||||
|
local_time = timezone.localtime(satellite.updated_at)
|
||||||
|
updated_at_str = local_time.strftime("%d.%m.%Y %H:%M")
|
||||||
|
|
||||||
|
# Get band names
|
||||||
|
bands = list(satellite.band.values_list('name', flat=True))
|
||||||
|
bands_str = ', '.join(bands) if bands else '-'
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'id': satellite.id,
|
||||||
|
'name': satellite.name,
|
||||||
|
'norad': satellite.norad if satellite.norad else None,
|
||||||
|
'bands': bands_str,
|
||||||
|
'undersat_point': satellite.undersat_point if satellite.undersat_point is not None else None,
|
||||||
|
'url': satellite.url or None,
|
||||||
|
'comment': satellite.comment or '-',
|
||||||
|
'launch_date': launch_date_str,
|
||||||
|
'created_at': created_at_str,
|
||||||
|
'updated_at': updated_at_str,
|
||||||
|
'created_by': str(satellite.created_by) if satellite.created_by else '-',
|
||||||
|
'updated_by': str(satellite.updated_by) if satellite.updated_by else '-',
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonResponse(data)
|
||||||
|
except Satellite.DoesNotExist:
|
||||||
|
return JsonResponse({'error': 'Спутник не найден'}, status=404)
|
||||||
|
except Exception as e:
|
||||||
|
return JsonResponse({'error': str(e)}, status=500)
|
||||||
|
|||||||
@@ -40,10 +40,18 @@ class KubsatView(LoginRequiredMixin, FormView):
|
|||||||
for source in sources:
|
for source in sources:
|
||||||
source_data = {
|
source_data = {
|
||||||
'source': source,
|
'source': source,
|
||||||
'objitems_data': []
|
'objitems_data': [],
|
||||||
|
'has_lyngsat': False,
|
||||||
|
'lyngsat_id': None
|
||||||
}
|
}
|
||||||
|
|
||||||
for objitem in source.source_objitems.all():
|
for objitem in source.source_objitems.all():
|
||||||
|
# Check if objitem has LyngSat source
|
||||||
|
if hasattr(objitem, 'lyngsat_source') and objitem.lyngsat_source:
|
||||||
|
source_data['has_lyngsat'] = True
|
||||||
|
source_data['lyngsat_id'] = objitem.lyngsat_source.id
|
||||||
|
|
||||||
|
objitem_matches_date = True
|
||||||
objitem_matches_date = True
|
objitem_matches_date = True
|
||||||
geo_date = None
|
geo_date = None
|
||||||
|
|
||||||
@@ -91,11 +99,12 @@ class KubsatView(LoginRequiredMixin, FormView):
|
|||||||
|
|
||||||
def apply_filters(self, filters):
|
def apply_filters(self, filters):
|
||||||
"""Применяет фильтры к queryset Source"""
|
"""Применяет фильтры к queryset Source"""
|
||||||
queryset = Source.objects.select_related('info').prefetch_related(
|
queryset = Source.objects.select_related('info', 'ownership').prefetch_related(
|
||||||
'source_objitems__parameter_obj__id_satellite',
|
'source_objitems__parameter_obj__id_satellite',
|
||||||
'source_objitems__parameter_obj__polarization',
|
'source_objitems__parameter_obj__polarization',
|
||||||
'source_objitems__parameter_obj__modulation',
|
'source_objitems__parameter_obj__modulation',
|
||||||
'source_objitems__transponder__sat_id'
|
'source_objitems__transponder__sat_id',
|
||||||
|
'source_objitems__lyngsat_source'
|
||||||
).annotate(objitem_count=Count('source_objitems'))
|
).annotate(objitem_count=Count('source_objitems'))
|
||||||
|
|
||||||
# Фильтр по спутникам
|
# Фильтр по спутникам
|
||||||
@@ -146,6 +155,10 @@ class KubsatView(LoginRequiredMixin, FormView):
|
|||||||
if filters.get('object_type'):
|
if filters.get('object_type'):
|
||||||
queryset = queryset.filter(info__in=filters['object_type'])
|
queryset = queryset.filter(info__in=filters['object_type'])
|
||||||
|
|
||||||
|
# Фильтр по принадлежности объекта
|
||||||
|
if filters.get('object_ownership'):
|
||||||
|
queryset = queryset.filter(ownership__in=filters['object_ownership'])
|
||||||
|
|
||||||
# Фильтр по количеству ObjItem
|
# Фильтр по количеству ObjItem
|
||||||
objitem_count = filters.get('objitem_count')
|
objitem_count = filters.get('objitem_count')
|
||||||
if objitem_count == '1':
|
if objitem_count == '1':
|
||||||
@@ -191,13 +204,37 @@ class KubsatExportView(LoginRequiredMixin, FormView):
|
|||||||
}
|
}
|
||||||
sources_objitems[objitem.source.id]['objitems'].append(objitem)
|
sources_objitems[objitem.source.id]['objitems'].append(objitem)
|
||||||
|
|
||||||
# Создаем Excel файл
|
# Создаем Excel файл с двумя листами
|
||||||
wb = Workbook()
|
wb = Workbook()
|
||||||
ws = wb.active
|
|
||||||
ws.title = "Кубсат"
|
|
||||||
|
|
||||||
# Заголовки
|
# Первый лист: "Предложения" (только основные данные)
|
||||||
headers = [
|
ws_proposals = wb.active
|
||||||
|
ws_proposals.title = "Предложения"
|
||||||
|
|
||||||
|
# Заголовки для листа "Предложения"
|
||||||
|
headers_proposals = [
|
||||||
|
'Дата',
|
||||||
|
'Широта, град',
|
||||||
|
'Долгота, град',
|
||||||
|
'Высота, м',
|
||||||
|
'Местоположение',
|
||||||
|
'ИСЗ',
|
||||||
|
'Прямой канал, МГц',
|
||||||
|
'Обратный канал, МГц',
|
||||||
|
'Перенос'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Стиль заголовков для листа "Предложения"
|
||||||
|
for col_num, header in enumerate(headers_proposals, 1):
|
||||||
|
cell = ws_proposals.cell(row=1, column=col_num, value=header)
|
||||||
|
cell.font = Font(bold=True)
|
||||||
|
cell.alignment = Alignment(horizontal='center', vertical='center')
|
||||||
|
|
||||||
|
# Второй лист: "Комментарий" (все данные)
|
||||||
|
ws_comments = wb.create_sheet(title="Комментарий")
|
||||||
|
|
||||||
|
# Заголовки для листа "Комментарий"
|
||||||
|
headers_comments = [
|
||||||
'Дата',
|
'Дата',
|
||||||
'Широта, град',
|
'Широта, град',
|
||||||
'Долгота, град',
|
'Долгота, град',
|
||||||
@@ -215,9 +252,9 @@ class KubsatExportView(LoginRequiredMixin, FormView):
|
|||||||
'Оператор'
|
'Оператор'
|
||||||
]
|
]
|
||||||
|
|
||||||
# Стиль заголовков
|
# Стиль заголовков для листа "Комментарий"
|
||||||
for col_num, header in enumerate(headers, 1):
|
for col_num, header in enumerate(headers_comments, 1):
|
||||||
cell = ws.cell(row=1, column=col_num, value=header)
|
cell = ws_comments.cell(row=1, column=col_num, value=header)
|
||||||
cell.font = Font(bold=True)
|
cell.font = Font(bold=True)
|
||||||
cell.alignment = Alignment(horizontal='center', vertical='center')
|
cell.alignment = Alignment(horizontal='center', vertical='center')
|
||||||
|
|
||||||
@@ -225,7 +262,8 @@ class KubsatExportView(LoginRequiredMixin, FormView):
|
|||||||
current_date = datetime.now().strftime('%d.%m.%Y')
|
current_date = datetime.now().strftime('%d.%m.%Y')
|
||||||
operator_name = f"{request.user.first_name} {request.user.last_name}" if request.user.first_name else request.user.username
|
operator_name = f"{request.user.first_name} {request.user.last_name}" if request.user.first_name else request.user.username
|
||||||
|
|
||||||
row_num = 2
|
row_num_proposals = 2
|
||||||
|
row_num_comments = 2
|
||||||
for source_id, data in sources_objitems.items():
|
for source_id, data in sources_objitems.items():
|
||||||
source = data['source']
|
source = data['source']
|
||||||
objitems_list = data['objitems']
|
objitems_list = data['objitems']
|
||||||
@@ -315,37 +353,50 @@ class KubsatExportView(LoginRequiredMixin, FormView):
|
|||||||
# Иначе показываем диапазон
|
# Иначе показываем диапазон
|
||||||
date_range_str = f"{min_date_str}-{max_date_str}"
|
date_range_str = f"{min_date_str}-{max_date_str}"
|
||||||
|
|
||||||
# Записываем строку
|
# Записываем строку на лист "Предложения" (только основные данные)
|
||||||
ws.cell(row=row_num, column=1, value=current_date)
|
ws_proposals.cell(row=row_num_proposals, column=1, value=current_date)
|
||||||
ws.cell(row=row_num, column=2, value=latitude)
|
ws_proposals.cell(row=row_num_proposals, column=2, value=latitude)
|
||||||
ws.cell(row=row_num, column=3, value=longitude)
|
ws_proposals.cell(row=row_num_proposals, column=3, value=longitude)
|
||||||
ws.cell(row=row_num, column=4, value=0.0)
|
ws_proposals.cell(row=row_num_proposals, column=4, value=0.0)
|
||||||
ws.cell(row=row_num, column=5, value=location)
|
ws_proposals.cell(row=row_num_proposals, column=5, value=location)
|
||||||
ws.cell(row=row_num, column=6, value=satellite_info)
|
ws_proposals.cell(row=row_num_proposals, column=6, value=satellite_info)
|
||||||
ws.cell(row=row_num, column=7, value=direct_channel)
|
ws_proposals.cell(row=row_num_proposals, column=7, value=direct_channel)
|
||||||
ws.cell(row=row_num, column=8, value=reverse_channel)
|
ws_proposals.cell(row=row_num_proposals, column=8, value=reverse_channel)
|
||||||
ws.cell(row=row_num, column=9, value=transfer)
|
ws_proposals.cell(row=row_num_proposals, column=9, value=transfer)
|
||||||
ws.cell(row=row_num, column=10, value=objitem_count)
|
|
||||||
ws.cell(row=row_num, column=11, value=date_range_str)
|
|
||||||
ws.cell(row=row_num, column=12, value=mirrors_str)
|
|
||||||
ws.cell(row=row_num, column=13, value='')
|
|
||||||
ws.cell(row=row_num, column=14, value='')
|
|
||||||
ws.cell(row=row_num, column=15, value=operator_name)
|
|
||||||
|
|
||||||
row_num += 1
|
# Записываем строку на лист "Комментарий" (все данные)
|
||||||
|
ws_comments.cell(row=row_num_comments, column=1, value=current_date)
|
||||||
|
ws_comments.cell(row=row_num_comments, column=2, value=latitude)
|
||||||
|
ws_comments.cell(row=row_num_comments, column=3, value=longitude)
|
||||||
|
ws_comments.cell(row=row_num_comments, column=4, value=0.0)
|
||||||
|
ws_comments.cell(row=row_num_comments, column=5, value=location)
|
||||||
|
ws_comments.cell(row=row_num_comments, column=6, value=satellite_info)
|
||||||
|
ws_comments.cell(row=row_num_comments, column=7, value=direct_channel)
|
||||||
|
ws_comments.cell(row=row_num_comments, column=8, value=reverse_channel)
|
||||||
|
ws_comments.cell(row=row_num_comments, column=9, value=transfer)
|
||||||
|
ws_comments.cell(row=row_num_comments, column=10, value=objitem_count)
|
||||||
|
ws_comments.cell(row=row_num_comments, column=11, value=date_range_str)
|
||||||
|
ws_comments.cell(row=row_num_comments, column=12, value=mirrors_str)
|
||||||
|
ws_comments.cell(row=row_num_comments, column=13, value='')
|
||||||
|
ws_comments.cell(row=row_num_comments, column=14, value='')
|
||||||
|
ws_comments.cell(row=row_num_comments, column=15, value=operator_name)
|
||||||
|
|
||||||
|
row_num_proposals += 1
|
||||||
|
row_num_comments += 1
|
||||||
|
|
||||||
# Автоширина колонок
|
# Автоширина колонок для обоих листов
|
||||||
for column in ws.columns:
|
for ws in [ws_proposals, ws_comments]:
|
||||||
max_length = 0
|
for column in ws.columns:
|
||||||
column_letter = column[0].column_letter
|
max_length = 0
|
||||||
for cell in column:
|
column_letter = column[0].column_letter
|
||||||
try:
|
for cell in column:
|
||||||
if len(str(cell.value)) > max_length:
|
try:
|
||||||
max_length = len(str(cell.value))
|
if len(str(cell.value)) > max_length:
|
||||||
except:
|
max_length = len(str(cell.value))
|
||||||
pass
|
except:
|
||||||
adjusted_width = min(max_length + 2, 50)
|
pass
|
||||||
ws.column_dimensions[column_letter].width = adjusted_width
|
adjusted_width = min(max_length + 2, 50)
|
||||||
|
ws.column_dimensions[column_letter].width = adjusted_width
|
||||||
|
|
||||||
# Сохраняем в BytesIO
|
# Сохраняем в BytesIO
|
||||||
output = BytesIO()
|
output = BytesIO()
|
||||||
|
|||||||
@@ -71,6 +71,25 @@ class LinkLyngsatSourcesView(LoginRequiredMixin, FormMessageMixin, FormView):
|
|||||||
matching_sources.sort(key=lambda x: abs(round(x.frequency, 1) - rounded_freq))
|
matching_sources.sort(key=lambda x: abs(round(x.frequency, 1) - rounded_freq))
|
||||||
objitem.lyngsat_source = matching_sources[0]
|
objitem.lyngsat_source = matching_sources[0]
|
||||||
objitem.save(update_fields=['lyngsat_source'])
|
objitem.save(update_fields=['lyngsat_source'])
|
||||||
|
|
||||||
|
# Update Source with ObjectInfo and ObjectOwnership for TV
|
||||||
|
if objitem.source:
|
||||||
|
from ..models import ObjectInfo, ObjectOwnership
|
||||||
|
try:
|
||||||
|
tv_type = ObjectInfo.objects.get(name="Стационарные")
|
||||||
|
tv_ownership = ObjectOwnership.objects.get(name="ТВ")
|
||||||
|
|
||||||
|
# Update source if not already set
|
||||||
|
if not objitem.source.info:
|
||||||
|
objitem.source.info = tv_type
|
||||||
|
if not objitem.source.ownership:
|
||||||
|
objitem.source.ownership = tv_ownership
|
||||||
|
|
||||||
|
objitem.source.save(update_fields=['info', 'ownership'])
|
||||||
|
except (ObjectInfo.DoesNotExist, ObjectOwnership.DoesNotExist):
|
||||||
|
# If types don't exist, skip this step
|
||||||
|
pass
|
||||||
|
|
||||||
linked_count += 1
|
linked_count += 1
|
||||||
|
|
||||||
messages.success(
|
messages.success(
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ class SourceListView(LoginRequiredMixin, View):
|
|||||||
has_coords_kupsat = request.GET.get("has_coords_kupsat")
|
has_coords_kupsat = request.GET.get("has_coords_kupsat")
|
||||||
has_coords_valid = request.GET.get("has_coords_valid")
|
has_coords_valid = request.GET.get("has_coords_valid")
|
||||||
has_coords_reference = request.GET.get("has_coords_reference")
|
has_coords_reference = request.GET.get("has_coords_reference")
|
||||||
has_lyngsat = request.GET.get("has_lyngsat")
|
|
||||||
selected_info = request.GET.getlist("info_id")
|
selected_info = request.GET.getlist("info_id")
|
||||||
|
selected_ownership = request.GET.getlist("ownership_id")
|
||||||
objitem_count_min = request.GET.get("objitem_count_min", "").strip()
|
objitem_count_min = request.GET.get("objitem_count_min", "").strip()
|
||||||
objitem_count_max = request.GET.get("objitem_count_max", "").strip()
|
objitem_count_max = request.GET.get("objitem_count_max", "").strip()
|
||||||
date_from = request.GET.get("date_from", "").strip()
|
date_from = request.GET.get("date_from", "").strip()
|
||||||
@@ -97,6 +97,10 @@ class SourceListView(LoginRequiredMixin, View):
|
|||||||
# Get all ObjectInfo for filter
|
# Get all ObjectInfo for filter
|
||||||
object_infos = ObjectInfo.objects.all().order_by("name")
|
object_infos = ObjectInfo.objects.all().order_by("name")
|
||||||
|
|
||||||
|
# Get all ObjectOwnership for filter
|
||||||
|
from ..models import ObjectOwnership
|
||||||
|
object_ownerships = ObjectOwnership.objects.all().order_by("name")
|
||||||
|
|
||||||
# Get all satellites that are used as mirrors
|
# Get all satellites that are used as mirrors
|
||||||
mirrors = (
|
mirrors = (
|
||||||
Satellite.objects.filter(geo_mirrors__isnull=False)
|
Satellite.objects.filter(geo_mirrors__isnull=False)
|
||||||
@@ -380,18 +384,14 @@ class SourceListView(LoginRequiredMixin, View):
|
|||||||
elif has_coords_reference == "0":
|
elif has_coords_reference == "0":
|
||||||
sources = sources.filter(coords_reference__isnull=True)
|
sources = sources.filter(coords_reference__isnull=True)
|
||||||
|
|
||||||
# Filter by LyngSat presence
|
|
||||||
if has_lyngsat == "1":
|
|
||||||
sources = sources.filter(source_objitems__lyngsat_source__isnull=False).distinct()
|
|
||||||
elif has_lyngsat == "0":
|
|
||||||
sources = sources.filter(
|
|
||||||
~Q(source_objitems__lyngsat_source__isnull=False)
|
|
||||||
).distinct()
|
|
||||||
|
|
||||||
# Filter by ObjectInfo (info field)
|
# Filter by ObjectInfo (info field)
|
||||||
if selected_info:
|
if selected_info:
|
||||||
sources = sources.filter(info_id__in=selected_info)
|
sources = sources.filter(info_id__in=selected_info)
|
||||||
|
|
||||||
|
# Filter by ObjectOwnership (ownership field)
|
||||||
|
if selected_ownership:
|
||||||
|
sources = sources.filter(ownership_id__in=selected_ownership)
|
||||||
|
|
||||||
# Filter by signal marks
|
# Filter by signal marks
|
||||||
if has_signal_mark or mark_date_from or mark_date_to:
|
if has_signal_mark or mark_date_from or mark_date_to:
|
||||||
mark_filter_q = Q()
|
mark_filter_q = Q()
|
||||||
@@ -648,13 +648,15 @@ class SourceListView(LoginRequiredMixin, View):
|
|||||||
'created_by': str(mark.created_by) if mark.created_by else '-',
|
'created_by': str(mark.created_by) if mark.created_by else '-',
|
||||||
})
|
})
|
||||||
|
|
||||||
# Get info name
|
# Get info name and ownership
|
||||||
info_name = source.info.name if source.info else '-'
|
info_name = source.info.name if source.info else '-'
|
||||||
|
ownership_name = source.ownership.name if source.ownership else '-'
|
||||||
|
|
||||||
processed_sources.append({
|
processed_sources.append({
|
||||||
'id': source.id,
|
'id': source.id,
|
||||||
'name': source_name if source_name else '-',
|
'name': source_name if source_name else '-',
|
||||||
'info': info_name,
|
'info': info_name,
|
||||||
|
'ownership': ownership_name,
|
||||||
'coords_average': coords_average_str,
|
'coords_average': coords_average_str,
|
||||||
'coords_kupsat': coords_kupsat_str,
|
'coords_kupsat': coords_kupsat_str,
|
||||||
'coords_valid': coords_valid_str,
|
'coords_valid': coords_valid_str,
|
||||||
@@ -682,10 +684,12 @@ class SourceListView(LoginRequiredMixin, View):
|
|||||||
'has_coords_kupsat': has_coords_kupsat,
|
'has_coords_kupsat': has_coords_kupsat,
|
||||||
'has_coords_valid': has_coords_valid,
|
'has_coords_valid': has_coords_valid,
|
||||||
'has_coords_reference': has_coords_reference,
|
'has_coords_reference': has_coords_reference,
|
||||||
'has_lyngsat': has_lyngsat,
|
|
||||||
'selected_info': [
|
'selected_info': [
|
||||||
int(x) if isinstance(x, str) else x for x in selected_info if (isinstance(x, int) or (isinstance(x, str) and x.isdigit()))
|
int(x) if isinstance(x, str) else x for x in selected_info if (isinstance(x, int) or (isinstance(x, str) and x.isdigit()))
|
||||||
],
|
],
|
||||||
|
'selected_ownership': [
|
||||||
|
int(x) if isinstance(x, str) else x for x in selected_ownership if (isinstance(x, int) or (isinstance(x, str) and x.isdigit()))
|
||||||
|
],
|
||||||
'objitem_count_min': objitem_count_min,
|
'objitem_count_min': objitem_count_min,
|
||||||
'objitem_count_max': objitem_count_max,
|
'objitem_count_max': objitem_count_max,
|
||||||
'date_from': date_from,
|
'date_from': date_from,
|
||||||
@@ -696,6 +700,8 @@ class SourceListView(LoginRequiredMixin, View):
|
|||||||
# ObjItem-level filters
|
# ObjItem-level filters
|
||||||
'geo_date_from': geo_date_from,
|
'geo_date_from': geo_date_from,
|
||||||
'geo_date_to': geo_date_to,
|
'geo_date_to': geo_date_to,
|
||||||
|
'object_infos': object_infos,
|
||||||
|
'object_ownerships': object_ownerships,
|
||||||
'satellites': satellites,
|
'satellites': satellites,
|
||||||
'selected_satellites': [
|
'selected_satellites': [
|
||||||
int(x) if isinstance(x, str) else x for x in selected_satellites if (isinstance(x, int) or (isinstance(x, str) and x.isdigit()))
|
int(x) if isinstance(x, str) else x for x in selected_satellites if (isinstance(x, int) or (isinstance(x, str) and x.isdigit()))
|
||||||
@@ -750,6 +756,9 @@ class SourceUpdateView(LoginRequiredMixin, AdminModeratorMixin, View):
|
|||||||
def get(self, request, pk):
|
def get(self, request, pk):
|
||||||
source = get_object_or_404(Source, pk=pk)
|
source = get_object_or_404(Source, pk=pk)
|
||||||
form = SourceForm(instance=source)
|
form = SourceForm(instance=source)
|
||||||
|
form.fields['average_latitude'].disabled = True
|
||||||
|
form.fields['average_longitude'].disabled = True
|
||||||
|
|
||||||
|
|
||||||
# Get related ObjItems ordered by creation date
|
# Get related ObjItems ordered by creation date
|
||||||
objitems = source.source_objitems.select_related(
|
objitems = source.source_objitems.select_related(
|
||||||
|
|||||||
Reference in New Issue
Block a user