Files
dbstorage/dbapp/mainapp/views/satellite.py

358 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Satellite 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 ..forms import SatelliteForm
from ..mixins import RoleRequiredMixin, FormMessageMixin
from ..models import Satellite, Band
from ..utils import parse_pagination_params
class SatelliteListView(LoginRequiredMixin, View):
"""View for displaying a list of satellites 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 name)
sort_param = request.GET.get("sort", "name")
# Get filter parameters
search_query = request.GET.get("search", "").strip()
selected_bands = request.GET.getlist("band_id")
norad_min = request.GET.get("norad_min", "").strip()
norad_max = request.GET.get("norad_max", "").strip()
undersat_point_min = request.GET.get("undersat_point_min", "").strip()
undersat_point_max = request.GET.get("undersat_point_max", "").strip()
launch_date_from = request.GET.get("launch_date_from", "").strip()
launch_date_to = request.GET.get("launch_date_to", "").strip()
date_from = request.GET.get("date_from", "").strip()
date_to = request.GET.get("date_to", "").strip()
# Get all bands for filters
bands = Band.objects.all().order_by("name")
# Get all satellites with query optimization
satellites = Satellite.objects.prefetch_related(
'band',
'created_by__user',
'updated_by__user'
).annotate(
transponder_count=Count('tran_satellite', distinct=True)
)
# Apply filters
# Filter by bands
if selected_bands:
satellites = satellites.filter(band__id__in=selected_bands).distinct()
# Filter by NORAD ID
if norad_min:
try:
min_val = int(norad_min)
satellites = satellites.filter(norad__gte=min_val)
except ValueError:
pass
if norad_max:
try:
max_val = int(norad_max)
satellites = satellites.filter(norad__lte=max_val)
except ValueError:
pass
# Filter by undersat point
if undersat_point_min:
try:
min_val = float(undersat_point_min)
satellites = satellites.filter(undersat_point__gte=min_val)
except ValueError:
pass
if undersat_point_max:
try:
max_val = float(undersat_point_max)
satellites = satellites.filter(undersat_point__lte=max_val)
except ValueError:
pass
# Filter by launch date range
if launch_date_from:
try:
date_from_obj = datetime.strptime(launch_date_from, "%Y-%m-%d").date()
satellites = satellites.filter(launch_date__gte=date_from_obj)
except (ValueError, TypeError):
pass
if launch_date_to:
try:
from datetime import timedelta
date_to_obj = datetime.strptime(launch_date_to, "%Y-%m-%d").date()
# Add one day to include entire end date
date_to_obj = date_to_obj + timedelta(days=1)
satellites = satellites.filter(launch_date__lt=date_to_obj)
except (ValueError, TypeError):
pass
# Filter by creation date range
if date_from:
try:
date_from_obj = datetime.strptime(date_from, "%Y-%m-%d")
satellites = satellites.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)
satellites = satellites.filter(created_at__lt=date_to_obj)
except (ValueError, TypeError):
pass
# Search by name
if search_query:
satellites = satellites.filter(
Q(name__icontains=search_query) |
Q(comment__icontains=search_query)
)
# Apply sorting
valid_sort_fields = {
"id": "id",
"-id": "-id",
"name": "name",
"-name": "-name",
"norad": "norad",
"-norad": "-norad",
"international_code": "international_code",
"-international_code": "-international_code",
"undersat_point": "undersat_point",
"-undersat_point": "-undersat_point",
"launch_date": "launch_date",
"-launch_date": "-launch_date",
"created_at": "created_at",
"-created_at": "-created_at",
"updated_at": "updated_at",
"-updated_at": "-updated_at",
"transponder_count": "transponder_count",
"-transponder_count": "-transponder_count",
}
if sort_param in valid_sort_fields:
satellites = satellites.order_by(valid_sort_fields[sort_param])
# Create paginator
paginator = Paginator(satellites, items_per_page)
page_obj = paginator.get_page(page_number)
# Prepare data for display
processed_satellites = []
for satellite in page_obj:
# Get band names
band_names = [band.name for band in satellite.band.all()]
processed_satellites.append({
'id': satellite.id,
'name': satellite.name or "-",
'norad': satellite.norad if satellite.norad else "-",
'international_code': satellite.international_code or "-",
'bands': ", ".join(band_names) if band_names else "-",
'undersat_point': f"{satellite.undersat_point:.2f}" if satellite.undersat_point is not None else "-",
'launch_date': satellite.launch_date.strftime("%d.%m.%Y") if satellite.launch_date else "-",
'url': satellite.url or "-",
'comment': satellite.comment or "-",
'transponder_count': satellite.transponder_count,
'created_at': satellite.created_at,
'updated_at': satellite.updated_at,
'created_by': satellite.created_by if satellite.created_by else "-",
'updated_by': satellite.updated_by if satellite.updated_by else "-",
})
# Prepare context for template
context = {
'page_obj': page_obj,
'processed_satellites': processed_satellites,
'items_per_page': items_per_page,
'available_items_per_page': [50, 100, 500, 1000],
'sort': sort_param,
'search_query': search_query,
'bands': bands,
'selected_bands': [
int(x) if isinstance(x, str) else x for x in selected_bands
if (isinstance(x, int) or (isinstance(x, str) and x.isdigit()))
],
'norad_min': norad_min,
'norad_max': norad_max,
'undersat_point_min': undersat_point_min,
'undersat_point_max': undersat_point_max,
'launch_date_from': launch_date_from,
'launch_date_to': launch_date_to,
'date_from': date_from,
'date_to': date_to,
'full_width_page': True,
}
return render(request, "mainapp/satellite_list.html", context)
class SatelliteCreateView(RoleRequiredMixin, FormMessageMixin, CreateView):
"""View for creating a new satellite."""
model = Satellite
form_class = SatelliteForm
template_name = "mainapp/satellite_form.html"
success_url = reverse_lazy("mainapp:satellite_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 SatelliteUpdateView(RoleRequiredMixin, FormMessageMixin, UpdateView):
"""View for updating an existing satellite."""
model = Satellite
form_class = SatelliteForm
template_name = "mainapp/satellite_form.html"
success_url = reverse_lazy("mainapp:satellite_list")
success_message = "Спутник успешно обновлен!"
required_roles = ["admin", "moderator"]
def get_context_data(self, **kwargs):
import json
context = super().get_context_data(**kwargs)
context['action'] = 'update'
context['title'] = f'Редактирование спутника: {self.object.name}'
# Get transponders for this satellite for frequency plan
from mapsapp.models import Transponders
transponders = Transponders.objects.filter(
sat_id=self.object
).select_related('polarization').order_by('downlink')
# Prepare transponder data for frequency plan visualization
transponder_data = []
for t in transponders:
if t.downlink and t.frequency_range:
transponder_data.append({
'id': t.id,
'name': t.name or f"TP-{t.id}",
'downlink': float(t.downlink),
'uplink': float(t.uplink) if t.uplink else None,
'frequency_range': float(t.frequency_range),
'polarization': t.polarization.name if t.polarization else '-',
'zone_name': t.zone_name or '-',
})
context['transponders'] = json.dumps(transponder_data)
context['transponder_count'] = len(transponder_data)
return context
def form_valid(self, form):
form.instance.updated_by = self.request.user.customuser
return super().form_valid(form)
class DeleteSelectedSatellitesView(RoleRequiredMixin, View):
"""View for deleting multiple selected satellites with confirmation."""
required_roles = ["admin", "moderator"]
def get(self, request):
"""Show confirmation page with details about satellites to be deleted."""
ids = request.GET.get("ids", "")
if not ids:
messages.error(request, "Не выбраны спутники для удаления")
return redirect('mainapp:satellite_list')
try:
id_list = [int(x) for x in ids.split(",") if x.isdigit()]
satellites = Satellite.objects.filter(id__in=id_list).prefetch_related(
'band'
).annotate(
transponder_count=Count('tran_satellite', distinct=True)
)
# Prepare detailed information about satellites
satellites_info = []
total_transponders = 0
for satellite in satellites:
transponder_count = satellite.transponder_count
total_transponders += transponder_count
satellites_info.append({
'id': satellite.id,
'name': satellite.name or "-",
'norad': satellite.norad if satellite.norad else "-",
'transponder_count': transponder_count,
})
context = {
'satellites_info': satellites_info,
'total_satellites': len(satellites_info),
'total_transponders': total_transponders,
'ids': ids,
}
return render(request, 'mainapp/satellite_bulk_delete_confirm.html', context)
except Exception as e:
messages.error(request, f'Ошибка при подготовке удаления: {str(e)}')
return redirect('mainapp:satellite_list')
def post(self, request):
"""Actually delete the selected satellites."""
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
satellites = Satellite.objects.filter(id__in=id_list)
deleted_count = satellites.count()
# Delete satellites (cascade will handle related objects)
satellites.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)