""" Transponder CRUD operations and related views. """ from datetime import datetime from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin from django.core.paginator import Paginator from django.db.models import Count, Q from django.http import JsonResponse from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse, reverse_lazy from django.views import View from django.views.generic import CreateView, UpdateView from mapsapp.models import Transponders from ..forms import TransponderForm from ..mixins import RoleRequiredMixin, FormMessageMixin from ..models import Satellite, Polarization from ..utils import parse_pagination_params class TransponderListView(LoginRequiredMixin, View): """View for displaying a list of transponders with filtering and pagination.""" def get(self, request): # Get pagination parameters page_number, items_per_page = parse_pagination_params(request) # Get sorting parameters (default to satellite and downlink) sort_param = request.GET.get("sort", "sat_id__name") # Get filter parameters search_query = request.GET.get("search", "").strip() selected_satellites = request.GET.getlist("satellite_id") selected_polarizations = request.GET.getlist("polarization") downlink_min = request.GET.get("downlink_min", "").strip() downlink_max = request.GET.get("downlink_max", "").strip() uplink_min = request.GET.get("uplink_min", "").strip() uplink_max = request.GET.get("uplink_max", "").strip() freq_range_min = request.GET.get("freq_range_min", "").strip() freq_range_max = request.GET.get("freq_range_max", "").strip() snr_min = request.GET.get("snr_min", "").strip() snr_max = request.GET.get("snr_max", "").strip() date_from = request.GET.get("date_from", "").strip() date_to = request.GET.get("date_to", "").strip() # Get all satellites and polarizations for filters satellites = Satellite.objects.filter( tran_satellite__isnull=False ).distinct().only("id", "name").order_by("name") polarizations = Polarization.objects.all().order_by("name") # Get all transponders with query optimization transponders = Transponders.objects.select_related( 'sat_id', 'polarization', 'created_by__user', 'updated_by__user' ).annotate( objitem_count=Count('transponder_objitems') ) # Apply filters # Filter by satellites if selected_satellites: transponders = transponders.filter(sat_id_id__in=selected_satellites) # Filter by polarizations if selected_polarizations: transponders = transponders.filter(polarization_id__in=selected_polarizations) # Filter by downlink frequency if downlink_min: try: min_val = float(downlink_min) transponders = transponders.filter(downlink__gte=min_val) except ValueError: pass if downlink_max: try: max_val = float(downlink_max) transponders = transponders.filter(downlink__lte=max_val) except ValueError: pass # Filter by uplink frequency if uplink_min: try: min_val = float(uplink_min) transponders = transponders.filter(uplink__gte=min_val) except ValueError: pass if uplink_max: try: max_val = float(uplink_max) transponders = transponders.filter(uplink__lte=max_val) except ValueError: pass # Filter by frequency range if freq_range_min: try: min_val = float(freq_range_min) transponders = transponders.filter(frequency_range__gte=min_val) except ValueError: pass if freq_range_max: try: max_val = float(freq_range_max) transponders = transponders.filter(frequency_range__lte=max_val) except ValueError: pass # Filter by SNR if snr_min: try: min_val = float(snr_min) transponders = transponders.filter(snr__gte=min_val) except ValueError: pass if snr_max: try: max_val = float(snr_max) transponders = transponders.filter(snr__lte=max_val) except ValueError: pass # Filter by creation date range if date_from: try: date_from_obj = datetime.strptime(date_from, "%Y-%m-%d") transponders = transponders.filter(created_at__gte=date_from_obj) except (ValueError, TypeError): pass if date_to: try: from datetime import timedelta date_to_obj = datetime.strptime(date_to, "%Y-%m-%d") # Add one day to include entire end date date_to_obj = date_to_obj + timedelta(days=1) transponders = transponders.filter(created_at__lt=date_to_obj) except (ValueError, TypeError): pass # Search by name or zone name if search_query: transponders = transponders.filter( Q(name__icontains=search_query) | Q(zone_name__icontains=search_query) | Q(sat_id__name__icontains=search_query) ) # Apply sorting valid_sort_fields = { "sat_id__name": "sat_id__name", "-sat_id__name": "-sat_id__name", "name": "name", "-name": "-name", "downlink": "downlink", "-downlink": "-downlink", "uplink": "uplink", "-uplink": "-uplink", "frequency_range": "frequency_range", "-frequency_range": "-frequency_range", "zone_name": "zone_name", "-zone_name": "-zone_name", "polarization__name": "polarization__name", "-polarization__name": "-polarization__name", "snr": "snr", "-snr": "-snr", "created_at": "created_at", "-created_at": "-created_at", "updated_at": "updated_at", "-updated_at": "-updated_at", "objitem_count": "objitem_count", "-objitem_count": "-objitem_count", } if sort_param in valid_sort_fields: transponders = transponders.order_by(valid_sort_fields[sort_param]) # Create paginator paginator = Paginator(transponders, items_per_page) page_obj = paginator.get_page(page_number) # Prepare data for display processed_transponders = [] for transponder in page_obj: processed_transponders.append({ 'id': transponder.id, 'name': transponder.name or "-", 'satellite': transponder.sat_id.name if transponder.sat_id else "-", 'satellite_id': transponder.sat_id.id if transponder.sat_id else None, 'downlink': f"{transponder.downlink:.3f}" if transponder.downlink else "-", 'uplink': f"{transponder.uplink:.3f}" if transponder.uplink else "-", 'frequency_range': f"{transponder.frequency_range:.3f}" if transponder.frequency_range else "-", 'transfer': f"{transponder.transfer:.3f}" if transponder.transfer else "-", 'zone_name': transponder.zone_name or "-", 'polarization': transponder.polarization.name if transponder.polarization else "-", 'snr': f"{transponder.snr:.1f}" if transponder.snr else "-", 'objitem_count': transponder.objitem_count, 'created_at': transponder.created_at, 'updated_at': transponder.updated_at, 'created_by': transponder.created_by if transponder.created_by else "-", 'updated_by': transponder.updated_by if transponder.updated_by else "-", }) # Prepare context for template context = { 'page_obj': page_obj, 'processed_transponders': processed_transponders, 'items_per_page': items_per_page, 'available_items_per_page': [50, 100, 500, 1000], 'sort': sort_param, 'search_query': search_query, 'satellites': satellites, 'polarizations': polarizations, '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())) ], 'selected_polarizations': [ int(x) if isinstance(x, str) else x for x in selected_polarizations if (isinstance(x, int) or (isinstance(x, str) and x.isdigit())) ], 'downlink_min': downlink_min, 'downlink_max': downlink_max, 'uplink_min': uplink_min, 'uplink_max': uplink_max, 'freq_range_min': freq_range_min, 'freq_range_max': freq_range_max, 'snr_min': snr_min, 'snr_max': snr_max, 'date_from': date_from, 'date_to': date_to, 'full_width_page': True, } return render(request, "mainapp/transponder_list.html", context) class TransponderCreateView(RoleRequiredMixin, FormMessageMixin, CreateView): """View for creating a new transponder.""" model = Transponders form_class = TransponderForm template_name = "mainapp/transponder_form.html" success_url = reverse_lazy("mainapp:transponder_list") success_message = "Транспондер успешно создан!" required_roles = ["admin", "moderator"] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['action'] = 'create' context['title'] = 'Создание транспондера' return context def form_valid(self, form): form.instance.created_by = self.request.user.customuser form.instance.updated_by = self.request.user.customuser return super().form_valid(form) class TransponderUpdateView(RoleRequiredMixin, FormMessageMixin, UpdateView): """View for updating an existing transponder.""" model = Transponders form_class = TransponderForm template_name = "mainapp/transponder_form.html" success_url = reverse_lazy("mainapp:transponder_list") success_message = "Транспондер успешно обновлен!" required_roles = ["admin", "moderator"] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['action'] = 'update' context['title'] = f'Редактирование транспондера #{self.object.id}' # Get related objitems count context['objitem_count'] = self.object.transponder_objitems.count() return context def form_valid(self, form): form.instance.updated_by = self.request.user.customuser return super().form_valid(form) class DeleteSelectedTranspondersView(RoleRequiredMixin, View): """View for deleting multiple selected transponders with confirmation.""" required_roles = ["admin", "moderator"] def get(self, request): """Show confirmation page with details about transponders to be deleted.""" ids = request.GET.get("ids", "") if not ids: messages.error(request, "Не выбраны транспондеры для удаления") return redirect('mainapp:transponder_list') try: id_list = [int(x) for x in ids.split(",") if x.isdigit()] transponders = Transponders.objects.filter(id__in=id_list).select_related( 'sat_id', 'polarization' ).annotate( objitem_count=Count('transponder_objitems') ) # Prepare detailed information about transponders transponders_info = [] total_objitems = 0 for transponder in transponders: objitem_count = transponder.objitem_count total_objitems += objitem_count transponders_info.append({ 'id': transponder.id, 'name': transponder.name or "-", 'satellite': transponder.sat_id.name if transponder.sat_id else "-", 'downlink': f"{transponder.downlink:.3f}" if transponder.downlink else "-", 'frequency_range': f"{transponder.frequency_range:.3f}" if transponder.frequency_range else "-", 'objitem_count': objitem_count, }) context = { 'transponders_info': transponders_info, 'total_transponders': len(transponders_info), 'total_objitems': total_objitems, 'ids': ids, } return render(request, 'mainapp/transponder_bulk_delete_confirm.html', context) except Exception as e: messages.error(request, f'Ошибка при подготовке удаления: {str(e)}') return redirect('mainapp:transponder_list') def post(self, request): """Actually delete the selected transponders.""" ids = request.POST.get("ids", "") if not ids: return JsonResponse({"error": "Нет ID для удаления"}, status=400) try: id_list = [int(x) for x in ids.split(",") if x.isdigit()] # Get count before deletion transponders = Transponders.objects.filter(id__in=id_list) deleted_count = transponders.count() # Delete transponders (cascade will handle related objitems) transponders.delete() messages.success( request, f'Успешно удалено транспондеров: {deleted_count}' ) return JsonResponse({ "success": True, "message": f"Успешно удалено транспондеров: {deleted_count}", "deleted_count": deleted_count, }) except Exception as e: return JsonResponse({"error": f"Ошибка при удалении: {str(e)}"}, status=500)