Правки и улучшения визуала. Добавил функционал отметок.
This commit is contained in:
@@ -1,75 +0,0 @@
|
||||
# Views Module Structure
|
||||
|
||||
This directory contains the refactored views from the original monolithic `views.py` file.
|
||||
|
||||
## File Organization
|
||||
|
||||
### `__init__.py`
|
||||
Central import file that exports all views for easy access. This allows other modules to import views using:
|
||||
```python
|
||||
from mainapp.views import ObjItemListView, custom_logout
|
||||
```
|
||||
|
||||
### `base.py`
|
||||
Basic views and utilities:
|
||||
- `ActionsPageView` - Displays the actions page
|
||||
- `custom_logout()` - Custom logout function
|
||||
|
||||
### `objitem.py`
|
||||
ObjItem CRUD operations and related views:
|
||||
- `ObjItemListView` - List view with filtering and pagination
|
||||
- `ObjItemFormView` - Base class for create/update operations
|
||||
- `ObjItemCreateView` - Create new ObjItem
|
||||
- `ObjItemUpdateView` - Update existing ObjItem
|
||||
- `ObjItemDeleteView` - Delete ObjItem
|
||||
- `ObjItemDetailView` - Read-only detail view
|
||||
- `DeleteSelectedObjectsView` - Bulk delete operation
|
||||
|
||||
### `data_import.py`
|
||||
Data import views for various formats:
|
||||
- `AddSatellitesView` - Add satellites to database
|
||||
- `AddTranspondersView` - Upload and parse transponder data from XML
|
||||
- `LoadExcelDataView` - Load data from Excel files
|
||||
- `LoadCsvDataView` - Load data from CSV files
|
||||
- `UploadVchLoadView` - Upload VCH load data from HTML
|
||||
- `LinkVchSigmaView` - Link VCH data with Sigma parameters
|
||||
- `ProcessKubsatView` - Process Kubsat event data
|
||||
|
||||
### `api.py`
|
||||
API endpoints for AJAX requests:
|
||||
- `GetLocationsView` - Get locations by satellite ID in GeoJSON format
|
||||
- `LyngsatDataAPIView` - Get LyngSat source data
|
||||
- `SigmaParameterDataAPIView` - Get SigmaParameter data
|
||||
- `SourceObjItemsAPIView` - Get ObjItems related to a Source
|
||||
- `LyngsatTaskStatusAPIView` - Get Celery task status
|
||||
|
||||
### `lyngsat.py`
|
||||
LyngSat related views:
|
||||
- `LinkLyngsatSourcesView` - Link LyngSat sources to objects
|
||||
- `FillLyngsatDataView` - Fill data from Lyngsat website
|
||||
- `LyngsatTaskStatusView` - Track Lyngsat data filling task status
|
||||
- `ClearLyngsatCacheView` - Clear LyngSat cache
|
||||
|
||||
### `source.py`
|
||||
Source related views:
|
||||
- `SourceListView` - List view for Source objects with filtering
|
||||
|
||||
### `map.py`
|
||||
Map related views:
|
||||
- `ShowMapView` - Display objects on map (admin interface)
|
||||
- `ShowSelectedObjectsMapView` - Display selected objects on map
|
||||
- `ClusterTestView` - Test view for clustering functionality
|
||||
|
||||
## Migration Notes
|
||||
|
||||
The original `views.py` has been renamed to `views_old.py` as a backup. All imports have been updated in:
|
||||
- `dbapp/mainapp/urls.py`
|
||||
- `dbapp/dbapp/urls.py`
|
||||
|
||||
## Benefits of This Structure
|
||||
|
||||
1. **Better Organization** - Related views are grouped together
|
||||
2. **Easier Maintenance** - Smaller files are easier to navigate and modify
|
||||
3. **Clear Responsibilities** - Each file has a specific purpose
|
||||
4. **Improved Testability** - Easier to write focused unit tests
|
||||
5. **Better Collaboration** - Multiple developers can work on different files without conflicts
|
||||
@@ -1,5 +1,5 @@
|
||||
# Import all views for easy access
|
||||
from .base import ActionsPageView, custom_logout
|
||||
from .base import ActionsPageView, HomeView, custom_logout
|
||||
from .objitem import (
|
||||
ObjItemListView,
|
||||
ObjItemCreateView,
|
||||
@@ -51,6 +51,7 @@ from .map import (
|
||||
__all__ = [
|
||||
# Base
|
||||
'ActionsPageView',
|
||||
'HomeView',
|
||||
'custom_logout',
|
||||
# ObjItem
|
||||
'ObjItemListView',
|
||||
|
||||
@@ -192,7 +192,9 @@ class SourceObjItemsAPIView(LoginRequiredMixin, View):
|
||||
'source_objitems__lyngsat_source',
|
||||
'source_objitems__transponder',
|
||||
'source_objitems__created_by__user',
|
||||
'source_objitems__updated_by__user'
|
||||
'source_objitems__updated_by__user',
|
||||
'marks',
|
||||
'marks__created_by__user'
|
||||
).get(id=source_id)
|
||||
|
||||
# Get all related ObjItems, sorted by created_at
|
||||
@@ -327,9 +329,25 @@ class SourceObjItemsAPIView(LoginRequiredMixin, View):
|
||||
'mirrors': mirrors,
|
||||
})
|
||||
|
||||
# Get marks for the source
|
||||
marks_data = []
|
||||
for mark in source.marks.all().order_by('-timestamp'):
|
||||
mark_timestamp = '-'
|
||||
if mark.timestamp:
|
||||
local_time = timezone.localtime(mark.timestamp)
|
||||
mark_timestamp = local_time.strftime("%d.%m.%Y %H:%M")
|
||||
|
||||
marks_data.append({
|
||||
'id': mark.id,
|
||||
'mark': mark.mark,
|
||||
'timestamp': mark_timestamp,
|
||||
'created_by': str(mark.created_by) if mark.created_by else '-',
|
||||
})
|
||||
|
||||
return JsonResponse({
|
||||
'source_id': source_id,
|
||||
'objitems': objitems_data
|
||||
'objitems': objitems_data,
|
||||
'marks': marks_data
|
||||
})
|
||||
except Source.DoesNotExist:
|
||||
return JsonResponse({'error': 'Источник не найден'}, status=404)
|
||||
|
||||
@@ -1,10 +1,491 @@
|
||||
"""
|
||||
Base views and utilities.
|
||||
"""
|
||||
from datetime import datetime, timedelta
|
||||
from django.contrib.auth import logout
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Count, Q
|
||||
from django.shortcuts import redirect, render
|
||||
from django.views import View
|
||||
|
||||
from ..models import Source, ObjItem, Satellite, Modulation, Polarization, ObjectMark
|
||||
from ..utils import parse_pagination_params
|
||||
|
||||
|
||||
class HomeView(LoginRequiredMixin, View):
|
||||
"""
|
||||
Main page with filters for displaying sources or objitems.
|
||||
"""
|
||||
|
||||
def get(self, request):
|
||||
# Get pagination parameters
|
||||
page_number, items_per_page = parse_pagination_params(request)
|
||||
|
||||
# Get display mode: 'sources' or 'objitems'
|
||||
display_mode = request.GET.get("display_mode", "sources")
|
||||
|
||||
# Get filter parameters
|
||||
selected_satellites = request.GET.getlist("satellite_id")
|
||||
date_from = request.GET.get("date_from", "").strip()
|
||||
date_to = request.GET.get("date_to", "").strip()
|
||||
freq_min = request.GET.get("freq_min", "").strip()
|
||||
freq_max = request.GET.get("freq_max", "").strip()
|
||||
range_min = request.GET.get("range_min", "").strip()
|
||||
range_max = request.GET.get("range_max", "").strip()
|
||||
selected_modulations = request.GET.getlist("modulation")
|
||||
selected_polarizations = request.GET.getlist("polarization")
|
||||
|
||||
# Source-specific filters
|
||||
has_coords_average = request.GET.get("has_coords_average")
|
||||
has_kupsat = request.GET.get("has_kupsat")
|
||||
has_valid = request.GET.get("has_valid")
|
||||
has_reference = request.GET.get("has_reference")
|
||||
|
||||
# ObjItem-specific filters
|
||||
has_geo = request.GET.get("has_geo")
|
||||
has_lyngsat = request.GET.get("has_lyngsat")
|
||||
|
||||
# Marks filters
|
||||
show_marks = request.GET.get("show_marks", "0")
|
||||
marks_date_from = request.GET.get("marks_date_from", "").strip()
|
||||
marks_date_to = request.GET.get("marks_date_to", "").strip()
|
||||
marks_status = request.GET.get("marks_status", "") # all, present, absent
|
||||
|
||||
# Get all satellites, modulations, polarizations for filters
|
||||
satellites = Satellite.objects.all().order_by("name")
|
||||
modulations = Modulation.objects.all().order_by("name")
|
||||
polarizations = Polarization.objects.all().order_by("name")
|
||||
|
||||
# Prepare context
|
||||
context = {
|
||||
'display_mode': display_mode,
|
||||
'satellites': satellites,
|
||||
'modulations': modulations,
|
||||
'polarizations': polarizations,
|
||||
'selected_satellites': [int(x) for x in selected_satellites if x.isdigit()],
|
||||
'selected_modulations': [int(x) for x in selected_modulations if x.isdigit()],
|
||||
'selected_polarizations': [int(x) for x in selected_polarizations if x.isdigit()],
|
||||
'date_from': date_from,
|
||||
'date_to': date_to,
|
||||
'freq_min': freq_min,
|
||||
'freq_max': freq_max,
|
||||
'range_min': range_min,
|
||||
'range_max': range_max,
|
||||
'has_coords_average': has_coords_average,
|
||||
'has_kupsat': has_kupsat,
|
||||
'has_valid': has_valid,
|
||||
'has_reference': has_reference,
|
||||
'has_geo': has_geo,
|
||||
'has_lyngsat': has_lyngsat,
|
||||
'show_marks': show_marks,
|
||||
'marks_date_from': marks_date_from,
|
||||
'marks_date_to': marks_date_to,
|
||||
'marks_status': marks_status,
|
||||
'items_per_page': items_per_page,
|
||||
'available_items_per_page': [50, 100, 500, 1000],
|
||||
'full_width_page': True,
|
||||
}
|
||||
|
||||
if display_mode == "objitems":
|
||||
# Display ObjItems
|
||||
queryset = self._get_objitems_queryset(
|
||||
selected_satellites, date_from, date_to,
|
||||
freq_min, freq_max, range_min, range_max,
|
||||
selected_modulations, selected_polarizations,
|
||||
has_geo, has_lyngsat
|
||||
)
|
||||
|
||||
paginator = Paginator(queryset, items_per_page)
|
||||
page_obj = paginator.get_page(page_number)
|
||||
|
||||
processed_objitems = self._process_objitems(
|
||||
page_obj, show_marks, marks_date_from, marks_date_to, marks_status
|
||||
)
|
||||
|
||||
context.update({
|
||||
'page_obj': page_obj,
|
||||
'processed_objitems': processed_objitems,
|
||||
})
|
||||
else:
|
||||
# Display Sources
|
||||
queryset = self._get_sources_queryset(
|
||||
selected_satellites, date_from, date_to,
|
||||
freq_min, freq_max, range_min, range_max,
|
||||
selected_modulations, selected_polarizations,
|
||||
has_coords_average, has_kupsat, has_valid, has_reference
|
||||
)
|
||||
|
||||
paginator = Paginator(queryset, items_per_page)
|
||||
page_obj = paginator.get_page(page_number)
|
||||
|
||||
processed_sources = self._process_sources(
|
||||
page_obj, show_marks, marks_date_from, marks_date_to, marks_status
|
||||
)
|
||||
|
||||
context.update({
|
||||
'page_obj': page_obj,
|
||||
'processed_sources': processed_sources,
|
||||
})
|
||||
|
||||
return render(request, "mainapp/home.html", context)
|
||||
|
||||
def _get_sources_queryset(self, selected_satellites, date_from, date_to,
|
||||
freq_min, freq_max, range_min, range_max,
|
||||
selected_modulations, selected_polarizations,
|
||||
has_coords_average, has_kupsat, has_valid, has_reference):
|
||||
"""Build queryset for sources with filters."""
|
||||
sources = Source.objects.prefetch_related(
|
||||
'source_objitems',
|
||||
'source_objitems__parameter_obj',
|
||||
'source_objitems__parameter_obj__id_satellite',
|
||||
'source_objitems__geo_obj'
|
||||
).annotate(objitem_count=Count('source_objitems'))
|
||||
|
||||
# Filter by satellites
|
||||
if selected_satellites:
|
||||
sources = sources.filter(
|
||||
source_objitems__parameter_obj__id_satellite_id__in=selected_satellites
|
||||
).distinct()
|
||||
|
||||
# Filter by date range (using Geo timestamps)
|
||||
if date_from or date_to:
|
||||
geo_filter = Q()
|
||||
if date_from:
|
||||
try:
|
||||
date_from_obj = datetime.strptime(date_from, "%Y-%m-%d")
|
||||
geo_filter &= Q(source_objitems__geo_obj__timestamp__gte=date_from_obj)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
if date_to:
|
||||
try:
|
||||
date_to_obj = datetime.strptime(date_to, "%Y-%m-%d") + timedelta(days=1)
|
||||
geo_filter &= Q(source_objitems__geo_obj__timestamp__lt=date_to_obj)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
if geo_filter:
|
||||
sources = sources.filter(geo_filter).distinct()
|
||||
|
||||
# Filter by frequency
|
||||
if freq_min:
|
||||
try:
|
||||
sources = sources.filter(
|
||||
source_objitems__parameter_obj__frequency__gte=float(freq_min)
|
||||
).distinct()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if freq_max:
|
||||
try:
|
||||
sources = sources.filter(
|
||||
source_objitems__parameter_obj__frequency__lte=float(freq_max)
|
||||
).distinct()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Filter by frequency range
|
||||
if range_min:
|
||||
try:
|
||||
sources = sources.filter(
|
||||
source_objitems__parameter_obj__freq_range__gte=float(range_min)
|
||||
).distinct()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if range_max:
|
||||
try:
|
||||
sources = sources.filter(
|
||||
source_objitems__parameter_obj__freq_range__lte=float(range_max)
|
||||
).distinct()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Filter by modulation
|
||||
if selected_modulations:
|
||||
sources = sources.filter(
|
||||
source_objitems__parameter_obj__modulation_id__in=selected_modulations
|
||||
).distinct()
|
||||
|
||||
# Filter by polarization
|
||||
if selected_polarizations:
|
||||
sources = sources.filter(
|
||||
source_objitems__parameter_obj__polarization_id__in=selected_polarizations
|
||||
).distinct()
|
||||
|
||||
# Filter by coordinates presence
|
||||
if has_coords_average == "1":
|
||||
sources = sources.filter(coords_average__isnull=False)
|
||||
elif has_coords_average == "0":
|
||||
sources = sources.filter(coords_average__isnull=True)
|
||||
|
||||
if has_kupsat == "1":
|
||||
sources = sources.filter(coords_kupsat__isnull=False)
|
||||
elif has_kupsat == "0":
|
||||
sources = sources.filter(coords_kupsat__isnull=True)
|
||||
|
||||
if has_valid == "1":
|
||||
sources = sources.filter(coords_valid__isnull=False)
|
||||
elif has_valid == "0":
|
||||
sources = sources.filter(coords_valid__isnull=True)
|
||||
|
||||
if has_reference == "1":
|
||||
sources = sources.filter(coords_reference__isnull=False)
|
||||
elif has_reference == "0":
|
||||
sources = sources.filter(coords_reference__isnull=True)
|
||||
|
||||
return sources.order_by('-id')
|
||||
|
||||
def _get_objitems_queryset(self, selected_satellites, date_from, date_to,
|
||||
freq_min, freq_max, range_min, range_max,
|
||||
selected_modulations, selected_polarizations,
|
||||
has_geo, has_lyngsat):
|
||||
"""Build queryset for objitems with filters."""
|
||||
objitems = ObjItem.objects.select_related(
|
||||
'parameter_obj',
|
||||
'parameter_obj__id_satellite',
|
||||
'parameter_obj__modulation',
|
||||
'parameter_obj__polarization',
|
||||
'geo_obj',
|
||||
'source',
|
||||
'lyngsat_source'
|
||||
)
|
||||
|
||||
# Filter by satellites
|
||||
if selected_satellites:
|
||||
objitems = objitems.filter(parameter_obj__id_satellite_id__in=selected_satellites)
|
||||
|
||||
# Filter by date range
|
||||
if date_from:
|
||||
try:
|
||||
date_from_obj = datetime.strptime(date_from, "%Y-%m-%d")
|
||||
objitems = objitems.filter(geo_obj__timestamp__gte=date_from_obj)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
if date_to:
|
||||
try:
|
||||
date_to_obj = datetime.strptime(date_to, "%Y-%m-%d") + timedelta(days=1)
|
||||
objitems = objitems.filter(geo_obj__timestamp__lt=date_to_obj)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# Filter by frequency
|
||||
if freq_min:
|
||||
try:
|
||||
objitems = objitems.filter(parameter_obj__frequency__gte=float(freq_min))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if freq_max:
|
||||
try:
|
||||
objitems = objitems.filter(parameter_obj__frequency__lte=float(freq_max))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Filter by frequency range
|
||||
if range_min:
|
||||
try:
|
||||
objitems = objitems.filter(parameter_obj__freq_range__gte=float(range_min))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if range_max:
|
||||
try:
|
||||
objitems = objitems.filter(parameter_obj__freq_range__lte=float(range_max))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Filter by modulation
|
||||
if selected_modulations:
|
||||
objitems = objitems.filter(parameter_obj__modulation_id__in=selected_modulations)
|
||||
|
||||
# Filter by polarization
|
||||
if selected_polarizations:
|
||||
objitems = objitems.filter(parameter_obj__polarization_id__in=selected_polarizations)
|
||||
|
||||
# Filter by coordinates presence
|
||||
if has_geo == "1":
|
||||
objitems = objitems.filter(geo_obj__isnull=False)
|
||||
elif has_geo == "0":
|
||||
objitems = objitems.filter(geo_obj__isnull=True)
|
||||
|
||||
# Filter by LyngSat connection
|
||||
if has_lyngsat == "1":
|
||||
objitems = objitems.filter(lyngsat_source__isnull=False)
|
||||
elif has_lyngsat == "0":
|
||||
objitems = objitems.filter(lyngsat_source__isnull=True)
|
||||
|
||||
return objitems.order_by('-id')
|
||||
|
||||
def _process_sources(self, page_obj, show_marks="0", marks_date_from="", marks_date_to="", marks_status=""):
|
||||
"""Process sources for display."""
|
||||
processed = []
|
||||
|
||||
for source in page_obj:
|
||||
# Get satellites
|
||||
satellite_names = set()
|
||||
for objitem in source.source_objitems.all():
|
||||
if objitem.parameter_obj and objitem.parameter_obj.id_satellite:
|
||||
satellite_names.add(objitem.parameter_obj.id_satellite.name)
|
||||
|
||||
# Format coordinates
|
||||
def format_coords(point):
|
||||
if point:
|
||||
lon, lat = point.coords[0], point.coords[1]
|
||||
lon_str = f"{lon}E" if lon > 0 else f"{abs(lon)}W"
|
||||
lat_str = f"{lat}N" if lat > 0 else f"{abs(lat)}S"
|
||||
return f"{lat_str} {lon_str}"
|
||||
return "-"
|
||||
|
||||
# Get marks if requested
|
||||
marks_data = []
|
||||
if show_marks == "1":
|
||||
marks_qs = source.marks.select_related('created_by__user').all()
|
||||
|
||||
# Filter marks by date
|
||||
if marks_date_from:
|
||||
try:
|
||||
date_from_obj = datetime.strptime(marks_date_from, "%Y-%m-%dT%H:%M")
|
||||
marks_qs = marks_qs.filter(timestamp__gte=date_from_obj)
|
||||
except (ValueError, TypeError):
|
||||
try:
|
||||
date_from_obj = datetime.strptime(marks_date_from, "%Y-%m-%d")
|
||||
marks_qs = marks_qs.filter(timestamp__gte=date_from_obj)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
if marks_date_to:
|
||||
try:
|
||||
date_to_obj = datetime.strptime(marks_date_to, "%Y-%m-%dT%H:%M")
|
||||
marks_qs = marks_qs.filter(timestamp__lte=date_to_obj)
|
||||
except (ValueError, TypeError):
|
||||
try:
|
||||
date_to_obj = datetime.strptime(marks_date_to, "%Y-%m-%d") + timedelta(days=1)
|
||||
marks_qs = marks_qs.filter(timestamp__lt=date_to_obj)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# Filter marks by status
|
||||
if marks_status == "present":
|
||||
marks_qs = marks_qs.filter(mark=True)
|
||||
elif marks_status == "absent":
|
||||
marks_qs = marks_qs.filter(mark=False)
|
||||
|
||||
# Process marks
|
||||
for mark in marks_qs:
|
||||
marks_data.append({
|
||||
'id': mark.id,
|
||||
'mark': mark.mark,
|
||||
'timestamp': mark.timestamp,
|
||||
'created_by': str(mark.created_by) if mark.created_by else "-",
|
||||
'can_edit': mark.can_edit(),
|
||||
})
|
||||
|
||||
processed.append({
|
||||
'id': source.id,
|
||||
'satellites': ", ".join(sorted(satellite_names)) if satellite_names else "-",
|
||||
'objitem_count': source.objitem_count,
|
||||
'coords_average': format_coords(source.coords_average),
|
||||
'coords_kupsat': format_coords(source.coords_kupsat),
|
||||
'coords_valid': format_coords(source.coords_valid),
|
||||
'coords_reference': format_coords(source.coords_reference),
|
||||
'created_at': source.created_at,
|
||||
'marks': marks_data,
|
||||
})
|
||||
|
||||
return processed
|
||||
|
||||
def _process_objitems(self, page_obj, show_marks="0", marks_date_from="", marks_date_to="", marks_status=""):
|
||||
"""Process objitems for display."""
|
||||
processed = []
|
||||
|
||||
for objitem in page_obj:
|
||||
param = objitem.parameter_obj
|
||||
geo = objitem.geo_obj
|
||||
source = objitem.source
|
||||
|
||||
# Format geo coordinates
|
||||
geo_coords = "-"
|
||||
geo_date = "-"
|
||||
if geo and geo.coords:
|
||||
lon, lat = geo.coords.coords[0], geo.coords.coords[1]
|
||||
lon_str = f"{lon}E" if lon > 0 else f"{abs(lon)}W"
|
||||
lat_str = f"{lat}N" if lat > 0 else f"{abs(lat)}S"
|
||||
geo_coords = f"{lat_str} {lon_str}"
|
||||
if geo.timestamp:
|
||||
geo_date = geo.timestamp.strftime("%Y-%m-%d")
|
||||
|
||||
# Format source coordinates
|
||||
def format_coords(point):
|
||||
if point:
|
||||
lon, lat = point.coords[0], point.coords[1]
|
||||
lon_str = f"{lon}E" if lon > 0 else f"{abs(lon)}W"
|
||||
lat_str = f"{lat}N" if lat > 0 else f"{abs(lat)}S"
|
||||
return f"{lat_str} {lon_str}"
|
||||
return "-"
|
||||
|
||||
kupsat_coords = format_coords(source.coords_kupsat) if source else "-"
|
||||
valid_coords = format_coords(source.coords_valid) if source else "-"
|
||||
|
||||
# Get marks if requested
|
||||
marks_data = []
|
||||
if show_marks == "1":
|
||||
marks_qs = objitem.marks.select_related('created_by__user').all()
|
||||
|
||||
# Filter marks by date
|
||||
if marks_date_from:
|
||||
try:
|
||||
date_from_obj = datetime.strptime(marks_date_from, "%Y-%m-%d")
|
||||
marks_qs = marks_qs.filter(timestamp__gte=date_from_obj)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
if marks_date_to:
|
||||
try:
|
||||
date_to_obj = datetime.strptime(marks_date_to, "%Y-%m-%d") + timedelta(days=1)
|
||||
marks_qs = marks_qs.filter(timestamp__lt=date_to_obj)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# Filter marks by status
|
||||
if marks_status == "present":
|
||||
marks_qs = marks_qs.filter(mark=True)
|
||||
elif marks_status == "absent":
|
||||
marks_qs = marks_qs.filter(mark=False)
|
||||
|
||||
# Process marks
|
||||
for mark in marks_qs:
|
||||
marks_data.append({
|
||||
'id': mark.id,
|
||||
'mark': mark.mark,
|
||||
'timestamp': mark.timestamp,
|
||||
'created_by': str(mark.created_by) if mark.created_by else "-",
|
||||
'can_edit': mark.can_edit(),
|
||||
})
|
||||
|
||||
processed.append({
|
||||
'id': objitem.id,
|
||||
'name': objitem.name or "-",
|
||||
'satellite': param.id_satellite.name if param and param.id_satellite else "-",
|
||||
'frequency': param.frequency if param else "-",
|
||||
'freq_range': param.freq_range if param else "-",
|
||||
'polarization': param.polarization.name if param and param.polarization else "-",
|
||||
'modulation': param.modulation.name if param and param.modulation else "-",
|
||||
'bod_velocity': param.bod_velocity if param else "-",
|
||||
'snr': param.snr if param else "-",
|
||||
'geo_coords': geo_coords,
|
||||
'geo_date': geo_date,
|
||||
'kupsat_coords': kupsat_coords,
|
||||
'valid_coords': valid_coords,
|
||||
'source_id': source.id if source else None,
|
||||
'lyngsat_id': objitem.lyngsat_source.id if objitem.lyngsat_source else None,
|
||||
'marks': marks_data,
|
||||
})
|
||||
|
||||
return processed
|
||||
|
||||
|
||||
class ActionsPageView(View):
|
||||
"""View for displaying the actions page."""
|
||||
|
||||
142
dbapp/mainapp/views/marks.py
Normal file
142
dbapp/mainapp/views/marks.py
Normal file
@@ -0,0 +1,142 @@
|
||||
"""
|
||||
Views для управления отметками объектов.
|
||||
"""
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.db.models import Prefetch
|
||||
from django.http import JsonResponse
|
||||
from django.views.generic import ListView, View
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from mainapp.models import Source, ObjectMark, CustomUser
|
||||
|
||||
|
||||
class ObjectMarksListView(LoginRequiredMixin, ListView):
|
||||
"""
|
||||
Представление списка источников с отметками.
|
||||
"""
|
||||
model = Source
|
||||
template_name = "mainapp/object_marks.html"
|
||||
context_object_name = "sources"
|
||||
paginate_by = 50
|
||||
|
||||
def get_queryset(self):
|
||||
"""Получить queryset с предзагруженными связанными данными"""
|
||||
queryset = Source.objects.prefetch_related(
|
||||
'source_objitems',
|
||||
'source_objitems__parameter_obj',
|
||||
'source_objitems__parameter_obj__id_satellite',
|
||||
Prefetch(
|
||||
'marks',
|
||||
queryset=ObjectMark.objects.select_related('created_by__user').order_by('-timestamp')
|
||||
)
|
||||
).order_by('-updated_at')
|
||||
|
||||
# Фильтрация по спутнику
|
||||
satellite_id = self.request.GET.get('satellite')
|
||||
if satellite_id:
|
||||
queryset = queryset.filter(source_objitems__parameter_obj__id_satellite_id=satellite_id).distinct()
|
||||
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Добавить дополнительные данные в контекст"""
|
||||
context = super().get_context_data(**kwargs)
|
||||
from mainapp.models import Satellite
|
||||
context['satellites'] = Satellite.objects.all().order_by('name')
|
||||
|
||||
# Добавить информацию о возможности редактирования для каждой отметки
|
||||
for source in context['sources']:
|
||||
for mark in source.marks.all():
|
||||
mark.editable = mark.can_edit()
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class AddObjectMarkView(LoginRequiredMixin, View):
|
||||
"""
|
||||
API endpoint для добавления отметки источника.
|
||||
"""
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""Создать новую отметку"""
|
||||
from datetime import timedelta
|
||||
from django.utils import timezone
|
||||
|
||||
source_id = request.POST.get('source_id')
|
||||
mark = request.POST.get('mark') == 'true'
|
||||
|
||||
if not source_id:
|
||||
return JsonResponse({'success': False, 'error': 'Не указан ID источника'}, status=400)
|
||||
|
||||
source = get_object_or_404(Source, pk=source_id)
|
||||
|
||||
# Проверить последнюю отметку источника
|
||||
last_mark = source.marks.first()
|
||||
if last_mark:
|
||||
time_diff = timezone.now() - last_mark.timestamp
|
||||
if time_diff < timedelta(minutes=5):
|
||||
minutes_left = 5 - int(time_diff.total_seconds() / 60)
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': f'Нельзя добавить отметку. Подождите ещё {minutes_left} мин.'
|
||||
}, status=400)
|
||||
|
||||
# Получить или создать CustomUser для текущего пользователя
|
||||
custom_user, _ = CustomUser.objects.get_or_create(user=request.user)
|
||||
|
||||
# Создать отметку
|
||||
object_mark = ObjectMark.objects.create(
|
||||
source=source,
|
||||
mark=mark,
|
||||
created_by=custom_user
|
||||
)
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'mark': {
|
||||
'id': object_mark.id,
|
||||
'mark': object_mark.mark,
|
||||
'timestamp': object_mark.timestamp.strftime('%d.%m.%Y %H:%M'),
|
||||
'created_by': str(object_mark.created_by) if object_mark.created_by else 'Неизвестно',
|
||||
'can_edit': object_mark.can_edit()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
class UpdateObjectMarkView(LoginRequiredMixin, View):
|
||||
"""
|
||||
API endpoint для обновления отметки объекта (в течение 5 минут).
|
||||
"""
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""Обновить существующую отметку"""
|
||||
mark_id = request.POST.get('mark_id')
|
||||
new_mark_value = request.POST.get('mark') == 'true'
|
||||
|
||||
if not mark_id:
|
||||
return JsonResponse({'success': False, 'error': 'Не указан ID отметки'}, status=400)
|
||||
|
||||
object_mark = get_object_or_404(ObjectMark, pk=mark_id)
|
||||
|
||||
# Проверить возможность редактирования
|
||||
if not object_mark.can_edit():
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Время редактирования истекло (более 5 минут)'
|
||||
}, status=400)
|
||||
|
||||
# Обновить отметку
|
||||
object_mark.mark = new_mark_value
|
||||
object_mark.save()
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'mark': {
|
||||
'id': object_mark.id,
|
||||
'mark': object_mark.mark,
|
||||
'timestamp': object_mark.timestamp.strftime('%d.%m.%Y %H:%M'),
|
||||
'created_by': str(object_mark.created_by) if object_mark.created_by else 'Неизвестно',
|
||||
'can_edit': object_mark.can_edit()
|
||||
}
|
||||
})
|
||||
@@ -57,7 +57,9 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
'source_objitems',
|
||||
'source_objitems__parameter_obj',
|
||||
'source_objitems__parameter_obj__id_satellite',
|
||||
'source_objitems__geo_obj'
|
||||
'source_objitems__geo_obj',
|
||||
'marks',
|
||||
'marks__created_by__user'
|
||||
).annotate(
|
||||
objitem_count=Count('source_objitems')
|
||||
)
|
||||
@@ -203,6 +205,15 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
|
||||
satellite_str = ", ".join(sorted(satellite_names)) if satellite_names else "-"
|
||||
|
||||
# Get all marks (presence/absence)
|
||||
marks_data = []
|
||||
for mark in source.marks.all():
|
||||
marks_data.append({
|
||||
'mark': mark.mark,
|
||||
'timestamp': mark.timestamp,
|
||||
'created_by': str(mark.created_by) if mark.created_by else '-',
|
||||
})
|
||||
|
||||
processed_sources.append({
|
||||
'id': source.id,
|
||||
'coords_average': coords_average_str,
|
||||
@@ -215,6 +226,7 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
'updated_at': source.updated_at,
|
||||
'has_lyngsat': has_lyngsat,
|
||||
'lyngsat_id': lyngsat_id,
|
||||
'marks': marks_data,
|
||||
})
|
||||
|
||||
# Prepare context for template
|
||||
|
||||
Reference in New Issue
Block a user