""" Представления для управления заявками на источники. """ from django.contrib.auth.mixins import LoginRequiredMixin from django.http import JsonResponse from django.views import View from django.views.generic import ListView, CreateView, UpdateView from django.urls import reverse_lazy from django.db.models import Q from mainapp.models import SourceRequest, SourceRequestStatusHistory, Source from mainapp.forms import SourceRequestForm class SourceRequestListView(LoginRequiredMixin, ListView): """Список заявок на источники.""" model = SourceRequest template_name = 'mainapp/source_request_list.html' context_object_name = 'requests' paginate_by = 50 def get_queryset(self): queryset = SourceRequest.objects.select_related( 'source', 'source__info', 'source__ownership', 'created_by__user', 'updated_by__user' ).order_by('-created_at') # Фильтр по статусу status = self.request.GET.get('status') if status: queryset = queryset.filter(status=status) # Фильтр по приоритету priority = self.request.GET.get('priority') if priority: queryset = queryset.filter(priority=priority) # Фильтр по источнику source_id = self.request.GET.get('source_id') if source_id: queryset = queryset.filter(source_id=source_id) # Фильтр по ГСО успешно gso_success = self.request.GET.get('gso_success') if gso_success == 'true': queryset = queryset.filter(gso_success=True) elif gso_success == 'false': queryset = queryset.filter(gso_success=False) # Фильтр по Кубсат успешно kubsat_success = self.request.GET.get('kubsat_success') if kubsat_success == 'true': queryset = queryset.filter(kubsat_success=True) elif kubsat_success == 'false': queryset = queryset.filter(kubsat_success=False) # Поиск search = self.request.GET.get('search') if search: queryset = queryset.filter( Q(source__id__icontains=search) | Q(comment__icontains=search) ) return queryset def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['status_choices'] = SourceRequest.STATUS_CHOICES context['priority_choices'] = SourceRequest.PRIORITY_CHOICES context['current_status'] = self.request.GET.get('status', '') context['current_priority'] = self.request.GET.get('priority', '') context['search_query'] = self.request.GET.get('search', '') context['form'] = SourceRequestForm() return context class SourceRequestCreateView(LoginRequiredMixin, CreateView): """Создание заявки на источник.""" model = SourceRequest form_class = SourceRequestForm template_name = 'mainapp/source_request_form.html' success_url = reverse_lazy('mainapp:source_request_list') def get_form_kwargs(self): kwargs = super().get_form_kwargs() # Передаем source_id если он есть в GET параметрах source_id = self.request.GET.get('source_id') if source_id: kwargs['source_id'] = source_id return kwargs def form_valid(self, form): # Устанавливаем created_by form.instance.created_by = getattr(self.request.user, 'customuser', None) form.instance.updated_by = getattr(self.request.user, 'customuser', None) response = super().form_valid(form) # Создаем начальную запись в истории SourceRequestStatusHistory.objects.create( source_request=self.object, old_status='', new_status=self.object.status, changed_by=form.instance.created_by, ) # Если это AJAX запрос, возвращаем JSON if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({ 'success': True, 'message': 'Заявка успешно создана', 'request_id': self.object.id }) return response def form_invalid(self, form): if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({ 'success': False, 'errors': form.errors }, status=400) return super().form_invalid(form) class SourceRequestUpdateView(LoginRequiredMixin, UpdateView): """Редактирование заявки на источник.""" model = SourceRequest form_class = SourceRequestForm template_name = 'mainapp/source_request_form.html' success_url = reverse_lazy('mainapp:source_request_list') def form_valid(self, form): # Устанавливаем updated_by form.instance.updated_by = getattr(self.request.user, 'customuser', None) response = super().form_valid(form) # Если это AJAX запрос, возвращаем JSON if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({ 'success': True, 'message': 'Заявка успешно обновлена', 'request_id': self.object.id }) return response def form_invalid(self, form): if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({ 'success': False, 'errors': form.errors }, status=400) return super().form_invalid(form) class SourceRequestDeleteView(LoginRequiredMixin, View): """Удаление заявки на источник.""" def post(self, request, pk): try: source_request = SourceRequest.objects.get(pk=pk) source_request.delete() return JsonResponse({ 'success': True, 'message': 'Заявка успешно удалена' }) except SourceRequest.DoesNotExist: return JsonResponse({ 'success': False, 'error': 'Заявка не найдена' }, status=404) class SourceRequestAPIView(LoginRequiredMixin, View): """API для получения данных о заявках источника.""" def get(self, request, source_id): try: source = Source.objects.get(pk=source_id) except Source.DoesNotExist: return JsonResponse({'error': 'Источник не найден'}, status=404) requests = SourceRequest.objects.filter(source=source).select_related( 'created_by__user', 'updated_by__user' ).prefetch_related('status_history__changed_by__user').order_by('-created_at') data = [] for req in requests: # Получаем историю статусов history = [] for h in req.status_history.all().order_by('-changed_at'): history.append({ 'old_status': h.get_old_status_display() if h.old_status else '-', 'new_status': h.get_new_status_display(), 'changed_at': h.changed_at.strftime('%d.%m.%Y %H:%M') if h.changed_at else '-', 'changed_by': str(h.changed_by) if h.changed_by else '-', }) data.append({ 'id': req.id, 'status': req.status, 'status_display': req.get_status_display(), 'priority': req.priority, 'priority_display': req.get_priority_display(), 'planned_at': req.planned_at.strftime('%d.%m.%Y %H:%M') if req.planned_at else '-', 'request_date': req.request_date.strftime('%d.%m.%Y') if req.request_date else '-', 'status_updated_at': req.status_updated_at.strftime('%d.%m.%Y %H:%M') if req.status_updated_at else '-', 'gso_success': req.gso_success, 'kubsat_success': req.kubsat_success, 'comment': req.comment or '-', 'created_at': req.created_at.strftime('%d.%m.%Y %H:%M') if req.created_at else '-', 'created_by': str(req.created_by) if req.created_by else '-', 'history': history, }) return JsonResponse({ 'source_id': source_id, 'requests': data, 'count': len(data) }) class SourceRequestDetailAPIView(LoginRequiredMixin, View): """API для получения детальной информации о заявке.""" def get(self, request, pk): try: req = SourceRequest.objects.select_related( 'source', 'source__info', 'source__ownership', 'created_by__user', 'updated_by__user' ).prefetch_related( 'status_history__changed_by__user', 'source__source_objitems__parameter_obj__modulation', 'source__source_objitems__geo_obj' ).get(pk=pk) except SourceRequest.DoesNotExist: return JsonResponse({'error': 'Заявка не найдена'}, status=404) # Получаем историю статусов history = [] for h in req.status_history.all().order_by('-changed_at'): history.append({ 'old_status': h.get_old_status_display() if h.old_status else '-', 'new_status': h.get_new_status_display(), 'changed_at': h.changed_at.strftime('%d.%m.%Y %H:%M') if h.changed_at else '-', 'changed_by': str(h.changed_by) if h.changed_by else '-', }) # Получаем данные из первой точки источника (имя, модуляция, символьная скорость) source_data = _get_source_extra_data(req.source) # Координаты из заявки или из источника coords_lat = None coords_lon = None if req.coords: coords_lat = req.coords.y coords_lon = req.coords.x elif req.source.coords_average: coords_lat = req.source.coords_average.y coords_lon = req.source.coords_average.x data = { 'id': req.id, 'source_id': req.source_id, 'status': req.status, 'status_display': req.get_status_display(), 'priority': req.priority, 'priority_display': req.get_priority_display(), 'planned_at': req.planned_at.isoformat() if req.planned_at else None, 'planned_at_display': req.planned_at.strftime('%d.%m.%Y %H:%M') if req.planned_at else '-', 'request_date': req.request_date.isoformat() if req.request_date else None, 'request_date_display': req.request_date.strftime('%d.%m.%Y') if req.request_date else '-', 'status_updated_at': req.status_updated_at.strftime('%d.%m.%Y %H:%M') if req.status_updated_at else '-', 'gso_success': req.gso_success, 'kubsat_success': req.kubsat_success, 'comment': req.comment or '', 'created_at': req.created_at.strftime('%d.%m.%Y %H:%M') if req.created_at else '-', 'created_by': str(req.created_by) if req.created_by else '-', 'history': history, # Дополнительные данные 'coords_lat': coords_lat, 'coords_lon': coords_lon, 'points_count': req.points_count, 'objitem_name': source_data['objitem_name'], 'modulation': source_data['modulation'], 'symbol_rate': source_data['symbol_rate'], } return JsonResponse(data) def _get_source_extra_data(source): """Получает дополнительные данные из первой точки источника.""" objitem_name = '-' modulation = '-' symbol_rate = '-' if source: # Получаем первую точку источника (сортируем по дате ГЛ) objitems = source.source_objitems.select_related( 'parameter_obj__modulation', 'geo_obj' ).order_by('geo_obj__timestamp') first_objitem = objitems.first() if first_objitem: objitem_name = first_objitem.name or '-' if first_objitem.parameter_obj: if first_objitem.parameter_obj.modulation: modulation = first_objitem.parameter_obj.modulation.name if first_objitem.parameter_obj.bod_velocity and first_objitem.parameter_obj.bod_velocity > 0: symbol_rate = str(int(first_objitem.parameter_obj.bod_velocity)) return { 'objitem_name': objitem_name, 'modulation': modulation, 'symbol_rate': symbol_rate, } class SourceDataAPIView(LoginRequiredMixin, View): """API для получения данных источника (координаты, имя точки, модуляция, символьная скорость).""" def get(self, request, source_id): from mainapp.utils import calculate_mean_coords from datetime import datetime try: source = Source.objects.select_related('info', 'ownership').prefetch_related( 'source_objitems__parameter_obj__modulation', 'source_objitems__geo_obj' ).get(pk=source_id) except Source.DoesNotExist: return JsonResponse({'error': 'Источник не найден', 'found': False}, status=404) # Получаем данные из точек источника source_data = _get_source_extra_data(source) # Рассчитываем усреднённые координаты из всех точек (сортируем по дате ГЛ) objitems = source.source_objitems.select_related('geo_obj').order_by('geo_obj__timestamp') avg_coords = None points_count = 0 for objitem in objitems: if hasattr(objitem, 'geo_obj') and objitem.geo_obj and objitem.geo_obj.coords: coord = (float(objitem.geo_obj.coords.x), float(objitem.geo_obj.coords.y)) points_count += 1 if avg_coords is None: avg_coords = coord else: avg_coords, _ = calculate_mean_coords(avg_coords, coord) # Если нет координат из точек, берём из источника coords_lat = None coords_lon = None if avg_coords: coords_lon = avg_coords[0] coords_lat = avg_coords[1] elif source.coords_average: coords_lat = source.coords_average.y coords_lon = source.coords_average.x data = { 'found': True, 'source_id': source_id, 'coords_lat': coords_lat, 'coords_lon': coords_lon, 'points_count': points_count, 'objitem_name': source_data['objitem_name'], 'modulation': source_data['modulation'], 'symbol_rate': source_data['symbol_rate'], 'info': source.info.name if source.info else '-', 'ownership': source.ownership.name if source.ownership else '-', } return JsonResponse(data)