Поменял теханализ, улучшения по простбам
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.paginator import Paginator
|
||||
from django.db import transaction
|
||||
from django.db.models import Q
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import render
|
||||
from django.views import View
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.db import transaction
|
||||
import json
|
||||
|
||||
from ..models import (
|
||||
@@ -11,157 +15,415 @@ from ..models import (
|
||||
Polarization,
|
||||
Modulation,
|
||||
Standard,
|
||||
ObjItem,
|
||||
Parameter,
|
||||
)
|
||||
from ..mixins import RoleRequiredMixin
|
||||
from ..utils import parse_pagination_params
|
||||
|
||||
|
||||
@login_required
|
||||
def tech_analyze_entry(request):
|
||||
class TechAnalyzeEntryView(LoginRequiredMixin, View):
|
||||
"""
|
||||
Представление для ввода данных технического анализа.
|
||||
"""
|
||||
satellites = Satellite.objects.all().order_by('name')
|
||||
|
||||
context = {
|
||||
'satellites': satellites,
|
||||
}
|
||||
|
||||
return render(request, 'mainapp/tech_analyze_entry.html', context)
|
||||
def get(self, request):
|
||||
satellites = Satellite.objects.all().order_by('name')
|
||||
|
||||
context = {
|
||||
'satellites': satellites,
|
||||
}
|
||||
|
||||
return render(request, 'mainapp/tech_analyze_entry.html', context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def tech_analyze_save(request):
|
||||
class TechAnalyzeSaveView(LoginRequiredMixin, View):
|
||||
"""
|
||||
API endpoint для сохранения данных технического анализа.
|
||||
"""
|
||||
try:
|
||||
data = json.loads(request.body)
|
||||
satellite_id = data.get('satellite_id')
|
||||
rows = data.get('rows', [])
|
||||
|
||||
if not satellite_id:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Не выбран спутник'
|
||||
}, status=400)
|
||||
|
||||
if not rows:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Нет данных для сохранения'
|
||||
}, status=400)
|
||||
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
satellite = Satellite.objects.get(id=satellite_id)
|
||||
except Satellite.DoesNotExist:
|
||||
data = json.loads(request.body)
|
||||
satellite_id = data.get('satellite_id')
|
||||
rows = data.get('rows', [])
|
||||
|
||||
if not satellite_id:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Не выбран спутник'
|
||||
}, status=400)
|
||||
|
||||
if not rows:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Нет данных для сохранения'
|
||||
}, status=400)
|
||||
|
||||
try:
|
||||
satellite = Satellite.objects.get(id=satellite_id)
|
||||
except Satellite.DoesNotExist:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Спутник не найден'
|
||||
}, status=404)
|
||||
|
||||
created_count = 0
|
||||
updated_count = 0
|
||||
errors = []
|
||||
|
||||
with transaction.atomic():
|
||||
for idx, row in enumerate(rows, start=1):
|
||||
try:
|
||||
name = row.get('name', '').strip()
|
||||
if not name:
|
||||
errors.append(f"Строка {idx}: отсутствует имя")
|
||||
continue
|
||||
|
||||
# Обработка поляризации
|
||||
polarization_name = row.get('polarization', '').strip() or '-'
|
||||
polarization, _ = Polarization.objects.get_or_create(name=polarization_name)
|
||||
|
||||
# Обработка модуляции
|
||||
modulation_name = row.get('modulation', '').strip() or '-'
|
||||
modulation, _ = Modulation.objects.get_or_create(name=modulation_name)
|
||||
|
||||
# Обработка стандарта
|
||||
standard_name = row.get('standard', '').strip()
|
||||
if standard_name.lower() == 'unknown':
|
||||
standard_name = '-'
|
||||
if not standard_name:
|
||||
standard_name = '-'
|
||||
standard, _ = Standard.objects.get_or_create(name=standard_name)
|
||||
|
||||
# Обработка числовых полей
|
||||
frequency = row.get('frequency')
|
||||
if frequency:
|
||||
try:
|
||||
frequency = float(str(frequency).replace(',', '.'))
|
||||
except (ValueError, TypeError):
|
||||
frequency = 0
|
||||
else:
|
||||
frequency = 0
|
||||
|
||||
freq_range = row.get('freq_range')
|
||||
if freq_range:
|
||||
try:
|
||||
freq_range = float(str(freq_range).replace(',', '.'))
|
||||
except (ValueError, TypeError):
|
||||
freq_range = 0
|
||||
else:
|
||||
freq_range = 0
|
||||
|
||||
bod_velocity = row.get('bod_velocity')
|
||||
if bod_velocity:
|
||||
try:
|
||||
bod_velocity = float(str(bod_velocity).replace(',', '.'))
|
||||
except (ValueError, TypeError):
|
||||
bod_velocity = 0
|
||||
else:
|
||||
bod_velocity = 0
|
||||
|
||||
note = row.get('note', '').strip()
|
||||
|
||||
# Создание или обновление записи
|
||||
tech_analyze, created = TechAnalyze.objects.update_or_create(
|
||||
name=name,
|
||||
defaults={
|
||||
'satellite': satellite,
|
||||
'polarization': polarization,
|
||||
'frequency': frequency,
|
||||
'freq_range': freq_range,
|
||||
'bod_velocity': bod_velocity,
|
||||
'modulation': modulation,
|
||||
'standard': standard,
|
||||
'note': note,
|
||||
'updated_by': request.user.customuser if hasattr(request.user, 'customuser') else None,
|
||||
}
|
||||
)
|
||||
|
||||
if created:
|
||||
tech_analyze.created_by = request.user.customuser if hasattr(request.user, 'customuser') else None
|
||||
tech_analyze.save()
|
||||
created_count += 1
|
||||
else:
|
||||
updated_count += 1
|
||||
|
||||
except Exception as e:
|
||||
errors.append(f"Строка {idx}: {str(e)}")
|
||||
|
||||
response_data = {
|
||||
'success': True,
|
||||
'created': created_count,
|
||||
'updated': updated_count,
|
||||
'total': created_count + updated_count,
|
||||
}
|
||||
|
||||
if errors:
|
||||
response_data['errors'] = errors
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Спутник не найден'
|
||||
}, status=404)
|
||||
|
||||
created_count = 0
|
||||
updated_count = 0
|
||||
errors = []
|
||||
|
||||
with transaction.atomic():
|
||||
for idx, row in enumerate(rows, start=1):
|
||||
try:
|
||||
name = row.get('name', '').strip()
|
||||
if not name:
|
||||
errors.append(f"Строка {idx}: отсутствует имя")
|
||||
continue
|
||||
|
||||
# Обработка поляризации
|
||||
polarization_name = row.get('polarization', '').strip() or '-'
|
||||
polarization, _ = Polarization.objects.get_or_create(name=polarization_name)
|
||||
|
||||
# Обработка модуляции
|
||||
modulation_name = row.get('modulation', '').strip() or '-'
|
||||
modulation, _ = Modulation.objects.get_or_create(name=modulation_name)
|
||||
|
||||
# Обработка стандарта
|
||||
standard_name = row.get('standard', '').strip()
|
||||
if standard_name.lower() == 'unknown':
|
||||
standard_name = '-'
|
||||
if not standard_name:
|
||||
standard_name = '-'
|
||||
standard, _ = Standard.objects.get_or_create(name=standard_name)
|
||||
|
||||
# Обработка числовых полей
|
||||
frequency = row.get('frequency')
|
||||
if frequency:
|
||||
try:
|
||||
frequency = float(str(frequency).replace(',', '.'))
|
||||
except (ValueError, TypeError):
|
||||
frequency = 0
|
||||
else:
|
||||
frequency = 0
|
||||
|
||||
freq_range = row.get('freq_range')
|
||||
if freq_range:
|
||||
try:
|
||||
freq_range = float(str(freq_range).replace(',', '.'))
|
||||
except (ValueError, TypeError):
|
||||
freq_range = 0
|
||||
else:
|
||||
freq_range = 0
|
||||
|
||||
bod_velocity = row.get('bod_velocity')
|
||||
if bod_velocity:
|
||||
try:
|
||||
bod_velocity = float(str(bod_velocity).replace(',', '.'))
|
||||
except (ValueError, TypeError):
|
||||
bod_velocity = 0
|
||||
else:
|
||||
bod_velocity = 0
|
||||
|
||||
note = row.get('note', '').strip()
|
||||
|
||||
# Создание или обновление записи
|
||||
tech_analyze, created = TechAnalyze.objects.update_or_create(
|
||||
name=name,
|
||||
defaults={
|
||||
'satellite': satellite,
|
||||
'polarization': polarization,
|
||||
'frequency': frequency,
|
||||
'freq_range': freq_range,
|
||||
'bod_velocity': bod_velocity,
|
||||
'modulation': modulation,
|
||||
'standard': standard,
|
||||
'note': note,
|
||||
'updated_by': request.user.customuser if hasattr(request.user, 'customuser') else None,
|
||||
}
|
||||
)
|
||||
|
||||
if created:
|
||||
tech_analyze.created_by = request.user.customuser if hasattr(request.user, 'customuser') else None
|
||||
tech_analyze.save()
|
||||
created_count += 1
|
||||
else:
|
||||
updated_count += 1
|
||||
'error': 'Неверный формат данных'
|
||||
}, status=400)
|
||||
except Exception as e:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
}, status=500)
|
||||
|
||||
|
||||
|
||||
class LinkExistingPointsView(LoginRequiredMixin, View):
|
||||
"""
|
||||
API endpoint для привязки существующих точек к данным теханализа.
|
||||
|
||||
Алгоритм:
|
||||
1. Получить все ObjItem для выбранного спутника
|
||||
2. Для каждого ObjItem:
|
||||
- Извлечь имя источника
|
||||
- Найти соответствующую запись TechAnalyze по имени и спутнику
|
||||
- Если найдена и данные отсутствуют в Parameter:
|
||||
* Обновить модуляцию (если "-")
|
||||
* Обновить символьную скорость (если -1.0 или None)
|
||||
* Обновить стандарт (если "-")
|
||||
"""
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
data = json.loads(request.body)
|
||||
satellite_id = data.get('satellite_id')
|
||||
|
||||
if not satellite_id:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Не выбран спутник'
|
||||
}, status=400)
|
||||
|
||||
try:
|
||||
satellite = Satellite.objects.get(id=satellite_id)
|
||||
except Satellite.DoesNotExist:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Спутник не найден'
|
||||
}, status=404)
|
||||
|
||||
# Получаем все ObjItem для данного спутника
|
||||
objitems = ObjItem.objects.filter(
|
||||
parameter_obj__id_satellite=satellite
|
||||
).select_related('parameter_obj', 'parameter_obj__modulation', 'parameter_obj__standard')
|
||||
|
||||
updated_count = 0
|
||||
skipped_count = 0
|
||||
errors = []
|
||||
|
||||
with transaction.atomic():
|
||||
for objitem in objitems:
|
||||
try:
|
||||
if not objitem.parameter_obj:
|
||||
skipped_count += 1
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
errors.append(f"Строка {idx}: {str(e)}")
|
||||
parameter = objitem.parameter_obj
|
||||
source_name = objitem.name
|
||||
|
||||
# Проверяем, нужно ли обновлять данные
|
||||
needs_update = (
|
||||
(parameter.modulation and parameter.modulation.name == "-") or
|
||||
parameter.bod_velocity is None or
|
||||
parameter.bod_velocity == -1.0 or
|
||||
parameter.bod_velocity == 0 or
|
||||
(parameter.standard and parameter.standard.name == "-")
|
||||
)
|
||||
|
||||
if not needs_update:
|
||||
skipped_count += 1
|
||||
continue
|
||||
|
||||
# Ищем данные в TechAnalyze по имени и спутнику
|
||||
tech_analyze = TechAnalyze.objects.filter(
|
||||
name=source_name,
|
||||
satellite=satellite
|
||||
).select_related('modulation', 'standard').first()
|
||||
|
||||
if not tech_analyze:
|
||||
skipped_count += 1
|
||||
continue
|
||||
|
||||
# Обновляем данные
|
||||
updated = False
|
||||
|
||||
# Обновляем модуляцию
|
||||
if parameter.modulation and parameter.modulation.name == "-" and tech_analyze.modulation:
|
||||
parameter.modulation = tech_analyze.modulation
|
||||
updated = True
|
||||
|
||||
# Обновляем символьную скорость
|
||||
if (parameter.bod_velocity is None or parameter.bod_velocity == -1.0 or parameter.bod_velocity == 0) and \
|
||||
tech_analyze.bod_velocity and tech_analyze.bod_velocity > 0:
|
||||
parameter.bod_velocity = tech_analyze.bod_velocity
|
||||
updated = True
|
||||
|
||||
# Обновляем стандарт
|
||||
if parameter.standard and parameter.standard.name == "-" and tech_analyze.standard:
|
||||
parameter.standard = tech_analyze.standard
|
||||
updated = True
|
||||
|
||||
if updated:
|
||||
parameter.save()
|
||||
updated_count += 1
|
||||
else:
|
||||
skipped_count += 1
|
||||
|
||||
except Exception as e:
|
||||
errors.append(f"ObjItem {objitem.id}: {str(e)}")
|
||||
|
||||
response_data = {
|
||||
'success': True,
|
||||
'updated': updated_count,
|
||||
'skipped': skipped_count,
|
||||
'total': objitems.count(),
|
||||
}
|
||||
|
||||
if errors:
|
||||
response_data['errors'] = errors
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Неверный формат данных'
|
||||
}, status=400)
|
||||
except Exception as e:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
}, status=500)
|
||||
|
||||
|
||||
|
||||
class TechAnalyzeListView(LoginRequiredMixin, View):
|
||||
"""
|
||||
Представление для отображения списка данных технического анализа.
|
||||
"""
|
||||
|
||||
def get(self, request):
|
||||
# Получаем список спутников для фильтра
|
||||
satellites = Satellite.objects.all().order_by('name')
|
||||
|
||||
response_data = {
|
||||
'success': True,
|
||||
'created': created_count,
|
||||
'updated': updated_count,
|
||||
'total': created_count + updated_count,
|
||||
# Получаем параметры из URL для передачи в шаблон
|
||||
search_query = request.GET.get('search', '').strip()
|
||||
satellite_ids = request.GET.getlist('satellite_id')
|
||||
items_per_page = int(request.GET.get('items_per_page', 50))
|
||||
|
||||
context = {
|
||||
'satellites': satellites,
|
||||
'selected_satellites': [int(sid) for sid in satellite_ids if sid],
|
||||
'search_query': search_query,
|
||||
'items_per_page': items_per_page,
|
||||
'available_items_per_page': [25, 50, 100, 200, 500],
|
||||
'full_width_page': True,
|
||||
}
|
||||
|
||||
if errors:
|
||||
response_data['errors'] = errors
|
||||
return render(request, 'mainapp/tech_analyze_list.html', context)
|
||||
|
||||
|
||||
class TechAnalyzeDeleteView(LoginRequiredMixin, RoleRequiredMixin, View):
|
||||
"""
|
||||
API endpoint для удаления выбранных записей теханализа.
|
||||
"""
|
||||
allowed_roles = ['admin', 'moderator']
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
data = json.loads(request.body)
|
||||
ids = data.get('ids', [])
|
||||
|
||||
if not ids:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Не выбраны записи для удаления'
|
||||
}, status=400)
|
||||
|
||||
# Удаляем записи
|
||||
deleted_count, _ = TechAnalyze.objects.filter(id__in=ids).delete()
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'deleted': deleted_count,
|
||||
'message': f'Удалено записей: {deleted_count}'
|
||||
})
|
||||
|
||||
except json.JSONDecodeError:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Неверный формат данных'
|
||||
}, status=400)
|
||||
except Exception as e:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
}, status=500)
|
||||
|
||||
|
||||
|
||||
class TechAnalyzeAPIView(LoginRequiredMixin, View):
|
||||
"""
|
||||
API endpoint для получения данных теханализа в формате для Tabulator.
|
||||
"""
|
||||
|
||||
def get(self, request):
|
||||
# Получаем параметры фильтрации
|
||||
search_query = request.GET.get('search', '').strip()
|
||||
satellite_ids = request.GET.getlist('satellite_id')
|
||||
|
||||
return JsonResponse(response_data)
|
||||
# Получаем параметры пагинации от Tabulator
|
||||
page = int(request.GET.get('page', 1))
|
||||
size = int(request.GET.get('size', 50))
|
||||
|
||||
# Базовый queryset
|
||||
tech_analyzes = TechAnalyze.objects.select_related(
|
||||
'satellite', 'polarization', 'modulation', 'standard', 'created_by', 'updated_by'
|
||||
).order_by('-created_at')
|
||||
|
||||
# Применяем фильтры
|
||||
if search_query:
|
||||
tech_analyzes = tech_analyzes.filter(
|
||||
Q(name__icontains=search_query) |
|
||||
Q(id__icontains=search_query)
|
||||
)
|
||||
|
||||
if satellite_ids:
|
||||
tech_analyzes = tech_analyzes.filter(satellite_id__in=satellite_ids)
|
||||
|
||||
# Пагинация
|
||||
paginator = Paginator(tech_analyzes, size)
|
||||
page_obj = paginator.get_page(page)
|
||||
|
||||
# Формируем данные для Tabulator
|
||||
results = []
|
||||
for item in page_obj:
|
||||
results.append({
|
||||
'id': item.id,
|
||||
'name': item.name or '',
|
||||
'satellite_id': item.satellite.id if item.satellite else None,
|
||||
'satellite_name': item.satellite.name if item.satellite else '-',
|
||||
'frequency': float(item.frequency) if item.frequency else 0,
|
||||
'freq_range': float(item.freq_range) if item.freq_range else 0,
|
||||
'bod_velocity': float(item.bod_velocity) if item.bod_velocity else 0,
|
||||
'polarization_name': item.polarization.name if item.polarization else '-',
|
||||
'modulation_name': item.modulation.name if item.modulation else '-',
|
||||
'standard_name': item.standard.name if item.standard else '-',
|
||||
'note': item.note or '',
|
||||
'created_at': item.created_at.isoformat() if item.created_at else None,
|
||||
'updated_at': item.updated_at.isoformat() if item.updated_at else None,
|
||||
})
|
||||
|
||||
except json.JSONDecodeError:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Неверный формат данных'
|
||||
}, status=400)
|
||||
except Exception as e:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
}, status=500)
|
||||
'last_page': paginator.num_pages,
|
||||
'data': results,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user