Сделал вкладку спутников
This commit is contained in:
353
dbapp/mainapp/views/satellite.py
Normal file
353
dbapp/mainapp/views/satellite.py
Normal file
@@ -0,0 +1,353 @@
|
||||
"""
|
||||
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",
|
||||
"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 "-",
|
||||
'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),
|
||||
'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)
|
||||
Reference in New Issue
Block a user