- |
+ |
{% if selected_satellite_id %}
Нет данных для выбранных фильтров
{% else %}
@@ -307,6 +421,30 @@
{% endblock %}
\ No newline at end of file
diff --git a/dbapp/mainapp/urls.py b/dbapp/mainapp/urls.py
index 036f1d1..e4f5012 100644
--- a/dbapp/mainapp/urls.py
+++ b/dbapp/mainapp/urls.py
@@ -18,6 +18,9 @@ urlpatterns = [
path('vch-upload/', views.UploadVchLoadView.as_view(), name='vch_load'),
path('vch-link/', views.LinkVchSigmaView.as_view(), name='link_vch_sigma'),
path('kubsat-excel/', views.ProcessKubsatView.as_view(), name='kubsat_excel'),
+ path('object/create/', views.ObjItemCreateView.as_view(), name='objitem_create'),
+ path('object//edit/', views.ObjItemUpdateView.as_view(), name='objitem_update'),
+ path('object//delete/', views.ObjItemDeleteView.as_view(), name='objitem_delete'),
# path('upload/', views.upload_file, name='upload_file'),
]
\ No newline at end of file
diff --git a/dbapp/mainapp/utils.py b/dbapp/mainapp/utils.py
index 09409c2..5824297 100644
--- a/dbapp/mainapp/utils.py
+++ b/dbapp/mainapp/utils.py
@@ -52,7 +52,7 @@ def remove_str(s: str):
return float(s.strip().replace(",", "."))
return s
-def fill_data_from_df(df: pd.DataFrame, sat: Satellite):
+def fill_data_from_df(df: pd.DataFrame, sat: Satellite, current_user=None):
try:
df.rename(columns={'Модуляция ': 'Модуляция'}, inplace=True)
except Exception as e:
@@ -111,6 +111,7 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite):
location = stroka[1]['Местоопределение'].strip()
comment = stroka[1]['Комментарий']
source = stroka[1]['Объект наблюдения']
+ user_to_use = current_user if current_user else CustomUser.objects.get(id=1)
vch_load_obj, _ = Parameter.objects.get_or_create(
id_satellite=sat,
@@ -120,7 +121,6 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite):
bod_velocity=v,
modulation=mod_obj,
snr=snr,
- # defaults={'id_user_add': CustomUser.objects.get(id=1)}
)
geo, _ = Geo.objects.get_or_create(
@@ -131,7 +131,7 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite):
'coords_valid': valid_point,
'location': location,
'comment': comment,
- 'is_average': (comment != -1.0)
+ 'is_average': (comment != -1.0),
}
)
geo.save()
@@ -144,7 +144,7 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite):
if not existing_obj_items.exists():
obj_item = ObjItem.objects.create(
name=source,
- id_user_add=CustomUser.objects.get(id=1)
+ created_by=user_to_use
)
obj_item.parameters_obj.set([vch_load_obj])
geo.objitem = obj_item
@@ -202,7 +202,7 @@ def get_point_from_json(filepath: str):
-def get_points_from_csv(file_content):
+def get_points_from_csv(file_content, current_user=None):
df = pd.read_csv(io.StringIO(file_content), sep=";",
names=['id', 'obj', 'lat', 'lon', 'h', 'time', 'sat', 'norad_id', 'freq', 'f_range', 'et', 'qaul', 'mir_1', 'mir_2', 'mir_3'])
df[['lat', 'lon', 'freq', 'f_range']] = df[['lat', 'lon', 'freq', 'f_range']].replace(',', '.', regex=True).astype(float)
@@ -239,12 +239,14 @@ def get_points_from_csv(file_content):
name=row['mir_3']
)
+ user_to_use = current_user if current_user else CustomUser.objects.get(id=1)
+
vch_load_obj, _ = Parameter.objects.get_or_create(
id_satellite=sat_obj,
polarization=pol_obj,
frequency=row['freq'],
freq_range=row['f_range'],
- # defaults={'id_user_add': CustomUser.objects.get(id=1)}
+ # defaults={'id_user_add': user_to_use}
)
geo_obj, _ = Geo.objects.get_or_create(
@@ -252,7 +254,7 @@ def get_points_from_csv(file_content):
coords=Point(row['lon'], row['lat'], srid=4326),
defaults={
'is_average': False,
- # 'id_user_add': CustomUser.objects.get(id=1),
+ # 'id_user_add': user_to_use,
}
)
geo_obj.mirrors.set(Mirror.objects.filter(name__in=mir_lst))
@@ -264,7 +266,7 @@ def get_points_from_csv(file_content):
if not existing_obj_items.exists():
obj_item = ObjItem.objects.create(
name=row['obj'],
- # id_user_add=CustomUser.objects.get(id=1)
+ created_by=user_to_use
)
obj_item.parameters_obj.set([vch_load_obj])
geo_obj.objitem = obj_item
diff --git a/dbapp/mainapp/views.py b/dbapp/mainapp/views.py
index 061160e..c0caf0a 100644
--- a/dbapp/mainapp/views.py
+++ b/dbapp/mainapp/views.py
@@ -6,10 +6,13 @@ from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views import View
-from django.views.generic import TemplateView, FormView
+from django.views.generic import TemplateView, FormView, UpdateView, DeleteView, CreateView
from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
from django.contrib.auth import logout
+from django.forms import inlineformset_factory, modelformset_factory
from django.db import models
+from django.urls import reverse_lazy
+from django.contrib.gis.geos import Point
import pandas as pd
from .utils import (
fill_data_from_df,
@@ -20,10 +23,21 @@ from .utils import (
kub_report
)
from mapsapp.utils import parse_transponders_from_json, parse_transponders_from_xml
-from .forms import LoadExcelData, LoadCsvData, UploadFileForm, VchLinkForm, UploadVchLoad, NewEventForm
+from .forms import (
+ LoadExcelData,
+ LoadCsvData,
+ UploadFileForm,
+ VchLinkForm,
+ UploadVchLoad,
+ NewEventForm,
+ ObjItemForm,
+ ParameterForm,
+ GeoForm
+)
from .models import ObjItem
from .clusters import get_clusters
from io import BytesIO
+from datetime import datetime
@@ -93,7 +107,7 @@ class LoadExcelDataView(LoginRequiredMixin, FormView):
df = pd.read_excel(io.BytesIO(uploaded_file.read()))
if number > 0:
df = df.head(number)
- result = fill_data_from_df(df, selected_sat)
+ result = fill_data_from_df(df, selected_sat, self.request.user.customuser)
messages.success(self.request, f"Данные успешно загружены! Обработано строк: {result}")
return redirect('load_excel_data')
@@ -151,7 +165,7 @@ class LoadCsvDataView(LoginRequiredMixin, FormView):
if isinstance(content, bytes):
content = content.decode('utf-8')
- get_points_from_csv(content)
+ get_points_from_csv(content, self.request.user.customuser)
messages.success(self.request, f"Данные успешно загружены!")
return redirect('load_csv_data')
except Exception as e:
@@ -331,7 +345,7 @@ class ObjItemListView(LoginRequiredMixin, View):
try:
items_per_page = int(items_per_page)
except ValueError:
- items_per_page = 25
+ items_per_page = 50
# Only filter objects by selected satellite if provided (no data shown by default)
objects = ObjItem.objects.none() # Initially empty
@@ -348,11 +362,11 @@ class ObjItemListView(LoginRequiredMixin, View):
# Start with the basic filter if any satellites are selected
if selected_satellites:
- # Start with the basic filter
+ # Start with the basic filter - optimized with prefetch_related for all related objects
objects = ObjItem.objects.select_related(
- 'id_user_add__user',
'geo_obj',
- 'updated_by__user'
+ 'updated_by__user',
+ 'created_by__user',
).prefetch_related(
'parameters_obj__id_satellite',
'parameters_obj__polarization',
@@ -362,9 +376,9 @@ class ObjItemListView(LoginRequiredMixin, View):
else:
# If no satellites are selected, start with all objects
objects = ObjItem.objects.select_related(
- 'id_user_add__user',
'geo_obj',
- 'updated_by__user'
+ 'updated_by__user',
+ 'created_by__user',
).prefetch_related(
'parameters_obj__id_satellite',
'parameters_obj__polarization',
@@ -468,8 +482,13 @@ class ObjItemListView(LoginRequiredMixin, View):
# Prepare the data to include calculated fields for the template
processed_objects = []
for obj in page_obj:
- # Get the first parameter
- param = obj.parameters_obj.first() if obj.parameters_obj.exists() else None
+ # Get the first parameter using the prefetched relation to avoid additional queries
+ param = None
+ if hasattr(obj, 'parameters_obj') and obj.parameters_obj.all():
+ # Get parameters from the prefetched queryset without triggering new query
+ param_list = list(obj.parameters_obj.all())
+ if param_list:
+ param = param_list[0] # Get first parameter without additional query
# Process geo coordinates
geo_coords = "-"
@@ -518,22 +537,51 @@ class ObjItemListView(LoginRequiredMixin, View):
if obj.geo_obj.distance_kup_valid is not None:
distance_kup_valid = f"{obj.geo_obj.distance_kup_valid:.3f}"
+ # Extract related object data to avoid additional queries in template
+ satellite_name = "-"
+ frequency = "-"
+ freq_range = "-"
+ polarization_name = "-"
+ bod_velocity = "-"
+ modulation_name = "-"
+ snr = "-"
+
+ if param:
+ # Get satellite data directly to avoid additional query
+ if hasattr(param, 'id_satellite') and param.id_satellite:
+ satellite_name = param.id_satellite.name if hasattr(param.id_satellite, 'name') else "-"
+
+ # Get parameter values directly
+ frequency = f"{param.frequency:.3f}" if param.frequency is not None else "-"
+ freq_range = f"{param.freq_range:.3f}" if param.freq_range is not None else "-"
+ bod_velocity = f"{param.bod_velocity:.0f}" if param.bod_velocity is not None else "-"
+ snr = f"{param.snr:.0f}" if param.snr is not None else "-"
+
+ # Get polarization name directly to avoid additional query
+ if hasattr(param, 'polarization') and param.polarization:
+ polarization_name = param.polarization.name if hasattr(param.polarization, 'name') else "-"
+
+ # Get modulation name directly to avoid additional query
+ if hasattr(param, 'modulation') and param.modulation:
+ modulation_name = param.modulation.name if hasattr(param.modulation, 'name') else "-"
+
processed_objects.append({
'id': obj.id,
'name': obj.name or "-",
- 'satellite_name': param.id_satellite.name if param and param.id_satellite else "-",
- 'frequency': f"{param.frequency:.3f}" if param and param.frequency else "-",
- 'freq_range': f"{param.freq_range:.3f}" if param and param.freq_range else "-",
- 'polarization': param.polarization.name if param and param.polarization else "-",
- 'bod_velocity': f"{param.bod_velocity:.0f}" if param and param.bod_velocity else "-",
- 'modulation': param.modulation.name if param and param.modulation else "-",
- 'snr': f"{param.snr:.0f}" if param and param.snr else "-",
+ 'satellite_name': satellite_name,
+ 'frequency': frequency,
+ 'freq_range': freq_range,
+ 'polarization': polarization_name,
+ 'bod_velocity': bod_velocity,
+ 'modulation': modulation_name,
+ 'snr': snr,
'geo_coords': geo_coords,
'kupsat_coords': kupsat_coords,
'valid_coords': valid_coords,
'distance_geo_kup': distance_geo_kup,
'distance_geo_valid': distance_geo_valid,
'distance_kup_valid': distance_kup_valid,
+ 'updated_by': obj.updated_by if obj.updated_by else '-',
'obj': obj
})
@@ -571,4 +619,209 @@ class ObjItemListView(LoginRequiredMixin, View):
'full_width_page': True,
}
- return render(request, 'mainapp/objitem_list.html', context)
\ No newline at end of file
+ return render(request, 'mainapp/objitem_list.html', context)
+
+class ObjItemUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
+ model = ObjItem
+ form_class = ObjItemForm
+ template_name = 'mainapp/objitem_form.html'
+ success_url = reverse_lazy('home')
+
+ def test_func(self):
+ return self.request.user.customuser.role in ['admin', 'moderator']
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+
+ # Добавляем контекст для карты
+ context['LEAFLET_CONFIG'] = {
+ 'DEFAULT_CENTER': (55.75, 37.62),
+ 'DEFAULT_ZOOM': 5,
+ }
+
+ # Остальной контекст остается без изменений
+ ParameterFormSet = modelformset_factory(
+ Parameter,
+ form=ParameterForm,
+ extra=0,
+ can_delete=True
+ )
+
+ if self.object:
+ parameter_queryset = self.object.parameters_obj.all()
+ context['parameter_forms'] = ParameterFormSet(
+ queryset=parameter_queryset,
+ prefix='parameters'
+ )
+
+ if hasattr(self.object, 'geo_obj'):
+ context['geo_form'] = GeoForm(instance=self.object.geo_obj, prefix='geo')
+ else:
+ context['geo_form'] = GeoForm(prefix='geo')
+ else:
+ context['parameter_forms'] = ParameterFormSet(
+ queryset=Parameter.objects.none(),
+ prefix='parameters'
+ )
+ context['geo_form'] = GeoForm(prefix='geo')
+
+ return context
+
+ def form_valid(self, form):
+ context = self.get_context_data()
+ parameter_forms = context['parameter_forms']
+ geo_form = context['geo_form']
+
+ # Сохраняем основной объект
+ self.object = form.save(commit=False)
+ self.object.updated_by = self.request.user.customuser
+ self.object.save()
+
+ # Сохраняем связанные параметры
+ if parameter_forms.is_valid():
+ instances = parameter_forms.save(commit=False)
+ for instance in instances:
+ instance.save()
+ instance.objitems.set([self.object])
+
+ # Сохраняем геоданные
+ geo_instance = None
+ if hasattr(self.object, 'geo_obj'):
+ geo_instance = self.object.geo_obj
+
+ # Создаем или обновляем гео-объект
+ if geo_instance is None:
+ geo_instance = Geo(objitem=self.object)
+
+ # Обновляем поля из geo_form
+ if geo_form.is_valid():
+ geo_instance.location = geo_form.cleaned_data['location']
+ geo_instance.comment = geo_form.cleaned_data['comment']
+ geo_instance.is_average = geo_form.cleaned_data['is_average']
+
+ # Обрабатываем координаты геолокации
+ geo_longitude = self.request.POST.get('geo_longitude')
+ geo_latitude = self.request.POST.get('geo_latitude')
+ if geo_longitude and geo_latitude:
+ geo_instance.coords = Point(float(geo_longitude), float(geo_latitude))
+
+ # Обрабатываем координаты Кубсата
+ kupsat_longitude = self.request.POST.get('kupsat_longitude')
+ kupsat_latitude = self.request.POST.get('kupsat_latitude')
+ if kupsat_longitude and kupsat_latitude:
+ geo_instance.coords_kupsat = Point(float(kupsat_longitude), float(kupsat_latitude))
+
+ # Обрабатываем координаты оперативников
+ valid_longitude = self.request.POST.get('valid_longitude')
+ valid_latitude = self.request.POST.get('valid_latitude')
+ if valid_longitude and valid_latitude:
+ geo_instance.coords_valid = Point(float(valid_longitude), float(valid_latitude))
+
+ # Обрабатываем дату/время
+ timestamp_date = self.request.POST.get('timestamp_date')
+ timestamp_time = self.request.POST.get('timestamp_time')
+ if timestamp_date and timestamp_time:
+ naive_datetime = datetime.strptime(f"{timestamp_date} {timestamp_time}", "%Y-%m-%d %H:%M")
+ geo_instance.timestamp = naive_datetime
+
+ geo_instance.save()
+
+ messages.success(self.request, 'Объект успешно сохранён!')
+ return super().form_valid(form)
+
+class ObjItemCreateView(LoginRequiredMixin, UserPassesTestMixin, CreateView):
+ model = ObjItem
+ form_class = ObjItemForm
+ template_name = 'mainapp/objitem_form.html'
+ success_url = reverse_lazy('home')
+
+ def test_func(self):
+ return self.request.user.customuser.role in ['admin', 'moderator']
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+
+ ParameterFormSet = modelformset_factory(
+ Parameter,
+ form=ParameterForm,
+ extra=1,
+ can_delete=True
+ )
+
+ context['parameter_forms'] = ParameterFormSet(
+ queryset=Parameter.objects.none(),
+ prefix='parameters'
+ )
+
+ context['geo_form'] = GeoForm(prefix='geo')
+
+ return context
+
+ def form_valid(self, form):
+ context = self.get_context_data()
+ parameter_forms = context['parameter_forms']
+ geo_form = context['geo_form']
+
+ # Сохраняем основной объект
+ self.object = form.save(commit=False)
+ self.object.created_by = self.request.user.customuser
+ self.object.updated_by = self.request.user.customuser
+ self.object.save()
+
+ # Сохраняем связанные параметры
+ if parameter_forms.is_valid():
+ instances = parameter_forms.save(commit=False)
+ for instance in instances:
+ instance.save()
+ instance.objitems.add(self.object)
+
+ # Создаем гео-объект
+ geo_instance = Geo(objitem=self.object)
+
+ # Обновляем поля из geo_form
+ if geo_form.is_valid():
+ geo_instance.location = geo_form.cleaned_data['location']
+ geo_instance.comment = geo_form.cleaned_data['comment']
+ geo_instance.is_average = geo_form.cleaned_data['is_average']
+
+ # Обрабатываем координаты геолокации
+ geo_longitude = self.request.POST.get('geo_longitude')
+ geo_latitude = self.request.POST.get('geo_latitude')
+ if geo_longitude and geo_latitude:
+ geo_instance.coords = Point(float(geo_longitude), float(geo_latitude))
+
+ # Обрабатываем координаты Кубсата
+ kupsat_longitude = self.request.POST.get('kupsat_longitude')
+ kupsat_latitude = self.request.POST.get('kupsat_latitude')
+ if kupsat_longitude and kupsat_latitude:
+ geo_instance.coords_kupsat = Point(float(kupsat_longitude), float(kupsat_latitude))
+
+ # Обрабатываем координаты оперативников
+ valid_longitude = self.request.POST.get('valid_longitude')
+ valid_latitude = self.request.POST.get('valid_latitude')
+ if valid_longitude and valid_latitude:
+ geo_instance.coords_valid = Point(float(valid_longitude), float(valid_latitude))
+
+ # Обрабатываем дату/время
+ timestamp_date = self.request.POST.get('timestamp_date')
+ timestamp_time = self.request.POST.get('timestamp_time')
+ if timestamp_date and timestamp_time:
+ naive_datetime = datetime.strptime(f"{timestamp_date} {timestamp_time}", "%Y-%m-%d %H:%M")
+ geo_instance.timestamp = naive_datetime
+
+ geo_instance.save()
+
+ messages.success(self.request, 'Объект успешно создан!')
+ return super().form_valid(form)
+
+class ObjItemDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
+ model = ObjItem
+ template_name = 'mainapp/objitem_confirm_delete.html'
+ success_url = reverse_lazy('home')
+
+ def test_func(self):
+ return self.request.user.customuser.role in ['admin', 'moderator']
+
+ def delete(self, request, *args, **kwargs):
+ messages.success(self.request, 'Объект успешно удалён!')
+ return super().delete(request, *args, **kwargs)
\ No newline at end of file
|