Внёс мелкие правки и фиксы
This commit is contained in:
@@ -570,3 +570,55 @@ class SatelliteDataAPIView(LoginRequiredMixin, View):
|
||||
return JsonResponse({'error': 'Спутник не найден'}, status=404)
|
||||
except Exception as e:
|
||||
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:
|
||||
source_data = {
|
||||
'source': source,
|
||||
'objitems_data': []
|
||||
'objitems_data': [],
|
||||
'has_lyngsat': False,
|
||||
'lyngsat_id': None
|
||||
}
|
||||
|
||||
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
|
||||
geo_date = None
|
||||
|
||||
@@ -91,11 +99,12 @@ class KubsatView(LoginRequiredMixin, FormView):
|
||||
|
||||
def apply_filters(self, filters):
|
||||
"""Применяет фильтры к 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__polarization',
|
||||
'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'))
|
||||
|
||||
# Фильтр по спутникам
|
||||
@@ -146,6 +155,10 @@ class KubsatView(LoginRequiredMixin, FormView):
|
||||
if filters.get('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_count = filters.get('objitem_count')
|
||||
if objitem_count == '1':
|
||||
@@ -191,13 +204,37 @@ class KubsatExportView(LoginRequiredMixin, FormView):
|
||||
}
|
||||
sources_objitems[objitem.source.id]['objitems'].append(objitem)
|
||||
|
||||
# Создаем Excel файл
|
||||
# Создаем Excel файл с двумя листами
|
||||
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):
|
||||
cell = ws.cell(row=1, column=col_num, value=header)
|
||||
# Стиль заголовков для листа "Комментарий"
|
||||
for col_num, header in enumerate(headers_comments, 1):
|
||||
cell = ws_comments.cell(row=1, column=col_num, value=header)
|
||||
cell.font = Font(bold=True)
|
||||
cell.alignment = Alignment(horizontal='center', vertical='center')
|
||||
|
||||
@@ -225,7 +262,8 @@ class KubsatExportView(LoginRequiredMixin, FormView):
|
||||
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
|
||||
|
||||
row_num = 2
|
||||
row_num_proposals = 2
|
||||
row_num_comments = 2
|
||||
for source_id, data in sources_objitems.items():
|
||||
source = data['source']
|
||||
objitems_list = data['objitems']
|
||||
@@ -315,37 +353,50 @@ class KubsatExportView(LoginRequiredMixin, FormView):
|
||||
# Иначе показываем диапазон
|
||||
date_range_str = f"{min_date_str}-{max_date_str}"
|
||||
|
||||
# Записываем строку
|
||||
ws.cell(row=row_num, column=1, value=current_date)
|
||||
ws.cell(row=row_num, column=2, value=latitude)
|
||||
ws.cell(row=row_num, column=3, value=longitude)
|
||||
ws.cell(row=row_num, column=4, value=0.0)
|
||||
ws.cell(row=row_num, column=5, value=location)
|
||||
ws.cell(row=row_num, column=6, value=satellite_info)
|
||||
ws.cell(row=row_num, column=7, value=direct_channel)
|
||||
ws.cell(row=row_num, column=8, value=reverse_channel)
|
||||
ws.cell(row=row_num, column=9, value=transfer)
|
||||
ws.cell(row=row_num, column=10, value=objitem_count)
|
||||
ws.cell(row=row_num, column=11, value=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)
|
||||
# Записываем строку на лист "Предложения" (только основные данные)
|
||||
ws_proposals.cell(row=row_num_proposals, column=1, value=current_date)
|
||||
ws_proposals.cell(row=row_num_proposals, column=2, value=latitude)
|
||||
ws_proposals.cell(row=row_num_proposals, column=3, value=longitude)
|
||||
ws_proposals.cell(row=row_num_proposals, column=4, value=0.0)
|
||||
ws_proposals.cell(row=row_num_proposals, column=5, value=location)
|
||||
ws_proposals.cell(row=row_num_proposals, column=6, value=satellite_info)
|
||||
ws_proposals.cell(row=row_num_proposals, column=7, value=direct_channel)
|
||||
ws_proposals.cell(row=row_num_proposals, column=8, value=reverse_channel)
|
||||
ws_proposals.cell(row=row_num_proposals, column=9, value=transfer)
|
||||
|
||||
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:
|
||||
max_length = 0
|
||||
column_letter = column[0].column_letter
|
||||
for cell in column:
|
||||
try:
|
||||
if len(str(cell.value)) > max_length:
|
||||
max_length = len(str(cell.value))
|
||||
except:
|
||||
pass
|
||||
adjusted_width = min(max_length + 2, 50)
|
||||
ws.column_dimensions[column_letter].width = adjusted_width
|
||||
# Автоширина колонок для обоих листов
|
||||
for ws in [ws_proposals, ws_comments]:
|
||||
for column in ws.columns:
|
||||
max_length = 0
|
||||
column_letter = column[0].column_letter
|
||||
for cell in column:
|
||||
try:
|
||||
if len(str(cell.value)) > max_length:
|
||||
max_length = len(str(cell.value))
|
||||
except:
|
||||
pass
|
||||
adjusted_width = min(max_length + 2, 50)
|
||||
ws.column_dimensions[column_letter].width = adjusted_width
|
||||
|
||||
# Сохраняем в 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))
|
||||
objitem.lyngsat_source = matching_sources[0]
|
||||
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
|
||||
|
||||
messages.success(
|
||||
|
||||
@@ -37,8 +37,8 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
has_coords_kupsat = request.GET.get("has_coords_kupsat")
|
||||
has_coords_valid = request.GET.get("has_coords_valid")
|
||||
has_coords_reference = request.GET.get("has_coords_reference")
|
||||
has_lyngsat = request.GET.get("has_lyngsat")
|
||||
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_max = request.GET.get("objitem_count_max", "").strip()
|
||||
date_from = request.GET.get("date_from", "").strip()
|
||||
@@ -97,6 +97,10 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
# Get all ObjectInfo for filter
|
||||
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
|
||||
mirrors = (
|
||||
Satellite.objects.filter(geo_mirrors__isnull=False)
|
||||
@@ -380,18 +384,14 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
elif has_coords_reference == "0":
|
||||
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)
|
||||
if 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
|
||||
if has_signal_mark or mark_date_from or mark_date_to:
|
||||
mark_filter_q = Q()
|
||||
@@ -648,13 +648,15 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
'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 '-'
|
||||
ownership_name = source.ownership.name if source.ownership else '-'
|
||||
|
||||
processed_sources.append({
|
||||
'id': source.id,
|
||||
'name': source_name if source_name else '-',
|
||||
'info': info_name,
|
||||
'ownership': ownership_name,
|
||||
'coords_average': coords_average_str,
|
||||
'coords_kupsat': coords_kupsat_str,
|
||||
'coords_valid': coords_valid_str,
|
||||
@@ -682,10 +684,12 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
'has_coords_kupsat': has_coords_kupsat,
|
||||
'has_coords_valid': has_coords_valid,
|
||||
'has_coords_reference': has_coords_reference,
|
||||
'has_lyngsat': has_lyngsat,
|
||||
'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()))
|
||||
],
|
||||
'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_max': objitem_count_max,
|
||||
'date_from': date_from,
|
||||
@@ -696,6 +700,8 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
# ObjItem-level filters
|
||||
'geo_date_from': geo_date_from,
|
||||
'geo_date_to': geo_date_to,
|
||||
'object_infos': object_infos,
|
||||
'object_ownerships': object_ownerships,
|
||||
'satellites': 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()))
|
||||
@@ -750,6 +756,9 @@ class SourceUpdateView(LoginRequiredMixin, AdminModeratorMixin, View):
|
||||
def get(self, request, pk):
|
||||
source = get_object_or_404(Source, pk=pk)
|
||||
form = SourceForm(instance=source)
|
||||
form.fields['average_latitude'].disabled = True
|
||||
form.fields['average_longitude'].disabled = True
|
||||
|
||||
|
||||
# Get related ObjItems ordered by creation date
|
||||
objitems = source.source_objitems.select_related(
|
||||
|
||||
Reference in New Issue
Block a user