Сделал деплой
This commit is contained in:
110
dbapp/Dockerfile
110
dbapp/Dockerfile
@@ -1,57 +1,53 @@
|
||||
FROM python:3.13-slim
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gdal-bin \
|
||||
libgdal-dev \
|
||||
proj-bin \
|
||||
proj-data \
|
||||
libproj-dev \
|
||||
libproj25 \
|
||||
libgeos-dev \
|
||||
libgeos-c1v5 \
|
||||
build-essential \
|
||||
postgresql-client \
|
||||
libpq-dev \
|
||||
libpq5 \
|
||||
netcat-openbsd \
|
||||
gcc \
|
||||
g++ \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set environment variables
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1
|
||||
|
||||
# Set work directory
|
||||
WORKDIR /app
|
||||
|
||||
# Upgrade pip
|
||||
RUN pip install --upgrade pip
|
||||
|
||||
# Copy requirements file
|
||||
COPY requirements.txt ./
|
||||
|
||||
# Install dependencies
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy project files
|
||||
COPY . .
|
||||
|
||||
# Create directories
|
||||
RUN mkdir -p /app/staticfiles /app/logs /app/media
|
||||
|
||||
# Set permissions for entrypoint
|
||||
RUN chmod +x /app/entrypoint.sh
|
||||
|
||||
# Create non-root user
|
||||
RUN useradd --create-home --shell /bin/bash app && \
|
||||
chown -R app:app /app
|
||||
|
||||
USER app
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8000
|
||||
|
||||
# Run entrypoint script
|
||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||
FROM python:3.13.7-slim AS builder
|
||||
|
||||
# Устанавливаем системные библиотеки для GIS, Postgres, сборки пакетов
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
gdal-bin libgdal-dev \
|
||||
libproj-dev proj-bin \
|
||||
libpq-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Устанавливаем uv пакетно-менеджер глобально
|
||||
RUN pip install --no-cache-dir uv
|
||||
|
||||
# Копируем зависимости
|
||||
COPY pyproject.toml uv.lock ./
|
||||
|
||||
# Синхронизируем зависимости (включая prod + dev), чтобы билдить
|
||||
RUN uv sync --locked
|
||||
|
||||
# Копируем весь код приложения
|
||||
COPY . .
|
||||
|
||||
# --- рантайм-стадия — минимальный образ для продакшена ---
|
||||
FROM python:3.13.7-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Устанавливаем только runtime-системные библиотеки
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gdal-bin \
|
||||
libproj-dev proj-bin \
|
||||
libpq5 \
|
||||
postgresql-client \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Копируем всё из билдера
|
||||
COPY --from=builder /usr/local/lib/python3.13 /usr/local/lib/python3.13
|
||||
COPY --from=builder /usr/local/bin /usr/local/bin
|
||||
COPY --from=builder /app /app
|
||||
|
||||
# Загружаем переменные окружения из .env (см. docker-compose)
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PATH="/usr/local/bin:$PATH"
|
||||
|
||||
# Делаем entrypoint.sh исполняемым
|
||||
RUN chmod +x /app/entrypoint.sh
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
# Используем entrypoint для инициализации (миграции, статика)
|
||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||
@@ -1,135 +1,141 @@
|
||||
"""
|
||||
Production-specific settings.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from .base import *
|
||||
|
||||
# ============================================================================
|
||||
# DEBUG CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
DEBUG = False
|
||||
|
||||
# ============================================================================
|
||||
# ALLOWED HOSTS
|
||||
# ============================================================================
|
||||
|
||||
# In production, specify allowed hosts explicitly from environment variable
|
||||
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "localhost,127.0.0.1").split(",")
|
||||
|
||||
# ============================================================================
|
||||
# SECURITY SETTINGS
|
||||
# ============================================================================
|
||||
|
||||
# SSL/HTTPS settings
|
||||
SECURE_SSL_REDIRECT = True
|
||||
SESSION_COOKIE_SECURE = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
|
||||
# Security headers
|
||||
SECURE_BROWSER_XSS_FILTER = True
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
|
||||
# HSTS settings
|
||||
SECURE_HSTS_SECONDS = 31536000 # 1 year
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
||||
SECURE_HSTS_PRELOAD = True
|
||||
|
||||
# Additional security settings
|
||||
SECURE_REDIRECT_EXEMPT = []
|
||||
X_FRAME_OPTIONS = "DENY"
|
||||
|
||||
# ============================================================================
|
||||
# TEMPLATE CACHING
|
||||
# ============================================================================
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [
|
||||
BASE_DIR / "templates",
|
||||
],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
"loaders": [
|
||||
(
|
||||
"django.template.loaders.cached.Loader",
|
||||
[
|
||||
"django.template.loaders.filesystem.Loader",
|
||||
"django.template.loaders.app_directories.Loader",
|
||||
],
|
||||
),
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
# ============================================================================
|
||||
# STATIC FILES CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
STATIC_ROOT = BASE_DIR.parent / "staticfiles"
|
||||
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"
|
||||
|
||||
# ============================================================================
|
||||
# LOGGING CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"verbose": {
|
||||
"format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
|
||||
"style": "{",
|
||||
},
|
||||
"simple": {
|
||||
"format": "{levelname} {message}",
|
||||
"style": "{",
|
||||
},
|
||||
},
|
||||
"filters": {
|
||||
"require_debug_false": {
|
||||
"()": "django.utils.log.RequireDebugFalse",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"level": "INFO",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "simple",
|
||||
},
|
||||
"file": {
|
||||
"level": "ERROR",
|
||||
"class": "logging.FileHandler",
|
||||
"filename": BASE_DIR.parent / "logs" / "django_errors.log",
|
||||
"formatter": "verbose",
|
||||
},
|
||||
"mail_admins": {
|
||||
"level": "ERROR",
|
||||
"class": "django.utils.log.AdminEmailHandler",
|
||||
"filters": ["require_debug_false"],
|
||||
"formatter": "verbose",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"django": {
|
||||
"handlers": ["console", "file"],
|
||||
"level": "INFO",
|
||||
"propagate": True,
|
||||
},
|
||||
"django.request": {
|
||||
"handlers": ["mail_admins", "file"],
|
||||
"level": "ERROR",
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
"""
|
||||
Production-specific settings.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from .base import *
|
||||
|
||||
# ============================================================================
|
||||
# DEBUG CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
DEBUG = False
|
||||
|
||||
# ============================================================================
|
||||
# ALLOWED HOSTS
|
||||
# ============================================================================
|
||||
|
||||
# In production, specify allowed hosts explicitly from environment variable
|
||||
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "localhost,127.0.0.1").split(",")
|
||||
|
||||
# CSRF trusted origins (required for forms to work behind proxy)
|
||||
CSRF_TRUSTED_ORIGINS = os.getenv(
|
||||
"CSRF_TRUSTED_ORIGINS",
|
||||
"http://localhost,http://127.0.0.1,http://localhost:8080,http://127.0.0.1:8080"
|
||||
).split(",")
|
||||
|
||||
# ============================================================================
|
||||
# SECURITY SETTINGS
|
||||
# ============================================================================
|
||||
|
||||
# SSL/HTTPS settings (disable for local testing without SSL)
|
||||
SECURE_SSL_REDIRECT = os.getenv("SECURE_SSL_REDIRECT", "False") == "True"
|
||||
SESSION_COOKIE_SECURE = os.getenv("SESSION_COOKIE_SECURE", "False") == "True"
|
||||
CSRF_COOKIE_SECURE = os.getenv("CSRF_COOKIE_SECURE", "False") == "True"
|
||||
|
||||
# Security headers
|
||||
SECURE_BROWSER_XSS_FILTER = True
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
|
||||
# HSTS settings (disable for local testing)
|
||||
SECURE_HSTS_SECONDS = int(os.getenv("SECURE_HSTS_SECONDS", "0"))
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = os.getenv("SECURE_HSTS_INCLUDE_SUBDOMAINS", "False") == "True"
|
||||
SECURE_HSTS_PRELOAD = os.getenv("SECURE_HSTS_PRELOAD", "False") == "True"
|
||||
|
||||
# Additional security settings
|
||||
SECURE_REDIRECT_EXEMPT = []
|
||||
X_FRAME_OPTIONS = "DENY"
|
||||
|
||||
# ============================================================================
|
||||
# TEMPLATE CACHING
|
||||
# ============================================================================
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [
|
||||
BASE_DIR / "templates",
|
||||
],
|
||||
"APP_DIRS": False, # Must be False when using custom loaders
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
"loaders": [
|
||||
(
|
||||
"django.template.loaders.cached.Loader",
|
||||
[
|
||||
"django.template.loaders.filesystem.Loader",
|
||||
"django.template.loaders.app_directories.Loader",
|
||||
],
|
||||
),
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
# ============================================================================
|
||||
# STATIC FILES CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
STATIC_ROOT = BASE_DIR.parent / "staticfiles"
|
||||
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"
|
||||
|
||||
# ============================================================================
|
||||
# LOGGING CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"verbose": {
|
||||
"format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
|
||||
"style": "{",
|
||||
},
|
||||
"simple": {
|
||||
"format": "{levelname} {message}",
|
||||
"style": "{",
|
||||
},
|
||||
},
|
||||
"filters": {
|
||||
"require_debug_false": {
|
||||
"()": "django.utils.log.RequireDebugFalse",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"level": "INFO",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "simple",
|
||||
},
|
||||
"file": {
|
||||
"level": "ERROR",
|
||||
"class": "logging.FileHandler",
|
||||
"filename": BASE_DIR.parent / "logs" / "django_errors.log",
|
||||
"formatter": "verbose",
|
||||
},
|
||||
"mail_admins": {
|
||||
"level": "ERROR",
|
||||
"class": "django.utils.log.AdminEmailHandler",
|
||||
"filters": ["require_debug_false"],
|
||||
"formatter": "verbose",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"django": {
|
||||
"handlers": ["console", "file"],
|
||||
"level": "INFO",
|
||||
"propagate": True,
|
||||
},
|
||||
"django.request": {
|
||||
"handlers": ["mail_admins", "file"],
|
||||
"level": "ERROR",
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,31 +1,36 @@
|
||||
"""
|
||||
URL configuration for dbapp project.
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/5.2/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from mainapp.views import custom_logout
|
||||
from django.contrib.auth import views as auth_views
|
||||
from debug_toolbar.toolbar import debug_toolbar_urls
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls, name='admin'),
|
||||
path('', include('mainapp.urls', namespace='mainapp')),
|
||||
path('', include('mapsapp.urls', namespace='mapsapp')),
|
||||
path('lyngsat/', include('lyngsatapp.urls', namespace='lyngsatapp')),
|
||||
# Authentication URLs
|
||||
path('login/', auth_views.LoginView.as_view(), name='login'),
|
||||
path('logout/', custom_logout, name='logout'),
|
||||
] + debug_toolbar_urls()
|
||||
"""
|
||||
URL configuration for dbapp project.
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/5.2/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from mainapp.views import custom_logout
|
||||
from django.contrib.auth import views as auth_views
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls, name='admin'),
|
||||
path('', include('mainapp.urls', namespace='mainapp')),
|
||||
path('', include('mapsapp.urls', namespace='mapsapp')),
|
||||
path('lyngsat/', include('lyngsatapp.urls', namespace='lyngsatapp')),
|
||||
# Authentication URLs
|
||||
path('login/', auth_views.LoginView.as_view(), name='login'),
|
||||
path('logout/', custom_logout, name='logout'),
|
||||
]
|
||||
|
||||
# Only include debug toolbar in development
|
||||
if settings.DEBUG:
|
||||
from debug_toolbar.toolbar import debug_toolbar_urls
|
||||
urlpatterns += debug_toolbar_urls()
|
||||
|
||||
74
dbapp/entrypoint.sh
Executable file → Normal file
74
dbapp/entrypoint.sh
Executable file → Normal file
@@ -1,37 +1,37 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Определяем окружение (по умолчанию production)
|
||||
ENVIRONMENT=${ENVIRONMENT:-production}
|
||||
|
||||
echo "Starting in $ENVIRONMENT mode..."
|
||||
|
||||
# Ждем PostgreSQL
|
||||
echo "Waiting for PostgreSQL..."
|
||||
while ! nc -z $DB_HOST $DB_PORT; do
|
||||
sleep 0.1
|
||||
done
|
||||
echo "PostgreSQL started"
|
||||
|
||||
# Выполняем миграции
|
||||
echo "Running migrations..."
|
||||
python manage.py migrate --noinput
|
||||
|
||||
# Собираем статику (только для production)
|
||||
if [ "$ENVIRONMENT" = "production" ]; then
|
||||
echo "Collecting static files..."
|
||||
python manage.py collectstatic --noinput
|
||||
fi
|
||||
|
||||
# Запускаем сервер в зависимости от окружения
|
||||
if [ "$ENVIRONMENT" = "development" ]; then
|
||||
echo "Starting Django development server..."
|
||||
exec python manage.py runserver 0.0.0.0:8000
|
||||
else
|
||||
echo "Starting Gunicorn..."
|
||||
exec gunicorn --bind 0.0.0.0:8000 \
|
||||
--workers ${GUNICORN_WORKERS:-3} \
|
||||
--timeout ${GUNICORN_TIMEOUT:-120} \
|
||||
--reload \
|
||||
dbapp.wsgi:application
|
||||
fi
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Определяем окружение (по умолчанию production)
|
||||
ENVIRONMENT=${ENVIRONMENT:-production}
|
||||
|
||||
echo "Starting in $ENVIRONMENT mode..."
|
||||
|
||||
# Ждем PostgreSQL
|
||||
echo "Waiting for PostgreSQL..."
|
||||
until PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -c '\q' 2>/dev/null; do
|
||||
echo "PostgreSQL is unavailable - sleeping"
|
||||
sleep 1
|
||||
done
|
||||
echo "PostgreSQL started"
|
||||
|
||||
# Выполняем миграции
|
||||
echo "Running migrations..."
|
||||
uv run python manage.py migrate --noinput
|
||||
|
||||
# Собираем статику (только для production)
|
||||
if [ "$ENVIRONMENT" = "production" ]; then
|
||||
echo "Collecting static files..."
|
||||
uv run python manage.py collectstatic --noinput
|
||||
fi
|
||||
|
||||
# Запускаем сервер в зависимости от окружения
|
||||
if [ "$ENVIRONMENT" = "development" ]; then
|
||||
echo "Starting Django development server..."
|
||||
exec uv run python manage.py runserver 0.0.0.0:8000
|
||||
else
|
||||
echo "Starting Gunicorn..."
|
||||
exec uv run gunicorn --bind 0.0.0.0:8000 \
|
||||
--workers ${GUNICORN_WORKERS:-3} \
|
||||
--timeout ${GUNICORN_TIMEOUT:-120} \
|
||||
dbapp.wsgi:application
|
||||
fi
|
||||
|
||||
@@ -1,285 +1,285 @@
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Q
|
||||
from django.views.generic import ListView
|
||||
|
||||
from .models import LyngSat
|
||||
from mainapp.models import Satellite, Polarization, Modulation, Standard
|
||||
from mainapp.utils import parse_pagination_params
|
||||
|
||||
|
||||
class LyngSatListView(LoginRequiredMixin, ListView):
|
||||
"""
|
||||
Представление для отображения списка источников LyngSat с фильтрацией и пагинацией.
|
||||
"""
|
||||
model = LyngSat
|
||||
template_name = 'lyngsatapp/lyngsat_list.html'
|
||||
context_object_name = 'lyngsat_items'
|
||||
paginate_by = 50
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Возвращает отфильтрованный и отсортированный queryset.
|
||||
"""
|
||||
queryset = LyngSat.objects.select_related(
|
||||
'id_satellite',
|
||||
'polarization',
|
||||
'modulation',
|
||||
'standard'
|
||||
).all()
|
||||
|
||||
# Поиск по ID
|
||||
search_query = self.request.GET.get('search', '').strip()
|
||||
if search_query:
|
||||
try:
|
||||
search_id = int(search_query)
|
||||
queryset = queryset.filter(id=search_id)
|
||||
except ValueError:
|
||||
queryset = queryset.none()
|
||||
|
||||
# Фильтр по спутнику
|
||||
satellite_ids = self.request.GET.getlist('satellite_id')
|
||||
if satellite_ids:
|
||||
queryset = queryset.filter(id_satellite_id__in=satellite_ids)
|
||||
|
||||
# Фильтр по поляризации
|
||||
polarization_ids = self.request.GET.getlist('polarization_id')
|
||||
if polarization_ids:
|
||||
queryset = queryset.filter(polarization_id__in=polarization_ids)
|
||||
|
||||
# Фильтр по модуляции
|
||||
modulation_ids = self.request.GET.getlist('modulation_id')
|
||||
if modulation_ids:
|
||||
queryset = queryset.filter(modulation_id__in=modulation_ids)
|
||||
|
||||
# Фильтр по стандарту
|
||||
standard_ids = self.request.GET.getlist('standard_id')
|
||||
if standard_ids:
|
||||
queryset = queryset.filter(standard_id__in=standard_ids)
|
||||
|
||||
# Фильтр по частоте
|
||||
freq_min = self.request.GET.get('freq_min', '').strip()
|
||||
freq_max = self.request.GET.get('freq_max', '').strip()
|
||||
if freq_min:
|
||||
try:
|
||||
queryset = queryset.filter(frequency__gte=float(freq_min))
|
||||
except ValueError:
|
||||
pass
|
||||
if freq_max:
|
||||
try:
|
||||
queryset = queryset.filter(frequency__lte=float(freq_max))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Фильтр по символьной скорости
|
||||
sym_min = self.request.GET.get('sym_min', '').strip()
|
||||
sym_max = self.request.GET.get('sym_max', '').strip()
|
||||
if sym_min:
|
||||
try:
|
||||
queryset = queryset.filter(sym_velocity__gte=float(sym_min))
|
||||
except ValueError:
|
||||
pass
|
||||
if sym_max:
|
||||
try:
|
||||
queryset = queryset.filter(sym_velocity__lte=float(sym_max))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Фильтр по дате обновления
|
||||
date_from = self.request.GET.get('date_from', '').strip()
|
||||
date_to = self.request.GET.get('date_to', '').strip()
|
||||
if date_from:
|
||||
queryset = queryset.filter(last_update__gte=date_from)
|
||||
if date_to:
|
||||
queryset = queryset.filter(last_update__lte=date_to)
|
||||
|
||||
# Сортировка
|
||||
sort = self.request.GET.get('sort', '-id')
|
||||
valid_sort_fields = ['id', '-id', 'frequency', '-frequency', 'sym_velocity', '-sym_velocity', 'last_update', '-last_update']
|
||||
if sort in valid_sort_fields:
|
||||
queryset = queryset.order_by(sort)
|
||||
else:
|
||||
queryset = queryset.order_by('-id')
|
||||
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
Добавляет дополнительный контекст для шаблона.
|
||||
"""
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
# Параметры пагинации
|
||||
page_number, items_per_page = parse_pagination_params(self.request, default_per_page=50)
|
||||
context['items_per_page'] = items_per_page
|
||||
context['available_items_per_page'] = [25, 50, 100, 200, 500]
|
||||
|
||||
# Пагинация
|
||||
paginator = Paginator(self.get_queryset(), items_per_page)
|
||||
page_obj = paginator.get_page(page_number)
|
||||
context['page_obj'] = page_obj
|
||||
context['lyngsat_items'] = page_obj.object_list
|
||||
|
||||
# Параметры поиска и фильтрации
|
||||
context['search_query'] = self.request.GET.get('search', '')
|
||||
context['sort'] = self.request.GET.get('sort', '-id')
|
||||
|
||||
# Данные для фильтров - только спутники с существующими записями LyngSat
|
||||
satellites = Satellite.objects.filter(
|
||||
lyngsat__isnull=False
|
||||
).distinct().order_by('name')
|
||||
polarizations = Polarization.objects.all().order_by('name')
|
||||
modulations = Modulation.objects.all().order_by('name')
|
||||
standards = Standard.objects.all().order_by('name')
|
||||
|
||||
# Выбранные фильтры
|
||||
selected_satellites = [int(x) for x in self.request.GET.getlist('satellite_id') if x.isdigit()]
|
||||
selected_polarizations = [int(x) for x in self.request.GET.getlist('polarization_id') if x.isdigit()]
|
||||
selected_modulations = [int(x) for x in self.request.GET.getlist('modulation_id') if x.isdigit()]
|
||||
selected_standards = [int(x) for x in self.request.GET.getlist('standard_id') if x.isdigit()]
|
||||
|
||||
# Параметры фильтров
|
||||
freq_min = self.request.GET.get('freq_min', '')
|
||||
freq_max = self.request.GET.get('freq_max', '')
|
||||
sym_min = self.request.GET.get('sym_min', '')
|
||||
sym_max = self.request.GET.get('sym_max', '')
|
||||
date_from = self.request.GET.get('date_from', '')
|
||||
date_to = self.request.GET.get('date_to', '')
|
||||
|
||||
# Action buttons HTML for toolbar component
|
||||
from django.urls import reverse
|
||||
action_buttons_html = f'''
|
||||
<a href="{reverse('mainapp:fill_lyngsat_data')}" class="btn btn-secondary btn-sm" title="Заполнить данные Lyngsat">
|
||||
<i class="bi bi-cloud-download"></i> Добавить данные
|
||||
</a>
|
||||
<a href="{reverse('mainapp:link_lyngsat')}" class="btn btn-primary btn-sm" title="Привязать источники LyngSat">
|
||||
<i class="bi bi-link-45deg"></i> Привязать
|
||||
</a>
|
||||
<a href="{reverse('mainapp:unlink_all_lyngsat')}" class="btn btn-warning btn-sm" title="Отвязать все источники LyngSat">
|
||||
<i class="bi bi-x-circle"></i> Отвязать
|
||||
</a>
|
||||
'''
|
||||
context['action_buttons_html'] = action_buttons_html
|
||||
|
||||
# Build filter HTML list for filter_panel component
|
||||
filter_html_list = []
|
||||
|
||||
# Satellite filter
|
||||
satellite_options = ''.join([
|
||||
f'<option value="{sat.id}" {"selected" if sat.id in selected_satellites else ""}>{sat.name}</option>'
|
||||
for sat in satellites
|
||||
])
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Спутник:</label>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('satellite_id', true)">Выбрать</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('satellite_id', false)">Снять</button>
|
||||
</div>
|
||||
<select name="satellite_id" class="form-select form-select-sm mb-2" multiple size="6">
|
||||
{satellite_options}
|
||||
</select>
|
||||
</div>
|
||||
''')
|
||||
|
||||
# Polarization filter
|
||||
polarization_options = ''.join([
|
||||
f'<option value="{pol.id}" {"selected" if pol.id in selected_polarizations else ""}>{pol.name}</option>'
|
||||
for pol in polarizations
|
||||
])
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Поляризация:</label>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('polarization_id', true)">Выбрать</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('polarization_id', false)">Снять</button>
|
||||
</div>
|
||||
<select name="polarization_id" class="form-select form-select-sm mb-2" multiple size="4">
|
||||
{polarization_options}
|
||||
</select>
|
||||
</div>
|
||||
''')
|
||||
|
||||
# Modulation filter
|
||||
modulation_options = ''.join([
|
||||
f'<option value="{mod.id}" {"selected" if mod.id in selected_modulations else ""}>{mod.name}</option>'
|
||||
for mod in modulations
|
||||
])
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Модуляция:</label>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('modulation_id', true)">Выбрать</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('modulation_id', false)">Снять</button>
|
||||
</div>
|
||||
<select name="modulation_id" class="form-select form-select-sm mb-2" multiple size="4">
|
||||
{modulation_options}
|
||||
</select>
|
||||
</div>
|
||||
''')
|
||||
|
||||
# Standard filter
|
||||
standard_options = ''.join([
|
||||
f'<option value="{std.id}" {"selected" if std.id in selected_standards else ""}>{std.name}</option>'
|
||||
for std in standards
|
||||
])
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Стандарт:</label>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('standard_id', true)">Выбрать</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('standard_id', false)">Снять</button>
|
||||
</div>
|
||||
<select name="standard_id" class="form-select form-select-sm mb-2" multiple size="4">
|
||||
{standard_options}
|
||||
</select>
|
||||
</div>
|
||||
''')
|
||||
|
||||
# Frequency filter
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Частота, МГц:</label>
|
||||
<input type="number" step="0.001" name="freq_min" class="form-control form-control-sm mb-1"
|
||||
placeholder="От" value="{freq_min}">
|
||||
<input type="number" step="0.001" name="freq_max" class="form-control form-control-sm"
|
||||
placeholder="До" value="{freq_max}">
|
||||
</div>
|
||||
''')
|
||||
|
||||
# Symbol rate filter
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Символьная скорость, БОД:</label>
|
||||
<input type="number" step="0.001" name="sym_min" class="form-control form-control-sm mb-1"
|
||||
placeholder="От" value="{sym_min}">
|
||||
<input type="number" step="0.001" name="sym_max" class="form-control form-control-sm"
|
||||
placeholder="До" value="{sym_max}">
|
||||
</div>
|
||||
''')
|
||||
|
||||
# Date filter
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Дата обновления:</label>
|
||||
<input type="date" name="date_from" id="date_from" class="form-control form-control-sm mb-1"
|
||||
placeholder="От" value="{date_from}">
|
||||
<input type="date" name="date_to" id="date_to" class="form-control form-control-sm"
|
||||
placeholder="До" value="{date_to}">
|
||||
</div>
|
||||
''')
|
||||
|
||||
context['filter_html_list'] = filter_html_list
|
||||
|
||||
# Enable full width layout
|
||||
context['full_width_page'] = True
|
||||
|
||||
return context
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Q
|
||||
from django.views.generic import ListView
|
||||
|
||||
from .models import LyngSat
|
||||
from mainapp.models import Satellite, Polarization, Modulation, Standard
|
||||
from mainapp.utils import parse_pagination_params
|
||||
|
||||
|
||||
class LyngSatListView(LoginRequiredMixin, ListView):
|
||||
"""
|
||||
Представление для отображения списка источников LyngSat с фильтрацией и пагинацией.
|
||||
"""
|
||||
model = LyngSat
|
||||
template_name = 'lyngsatapp/lyngsat_list.html'
|
||||
context_object_name = 'lyngsat_items'
|
||||
paginate_by = 50
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Возвращает отфильтрованный и отсортированный queryset.
|
||||
"""
|
||||
queryset = LyngSat.objects.select_related(
|
||||
'id_satellite',
|
||||
'polarization',
|
||||
'modulation',
|
||||
'standard'
|
||||
).all()
|
||||
|
||||
# Поиск по ID
|
||||
search_query = self.request.GET.get('search', '').strip()
|
||||
if search_query:
|
||||
try:
|
||||
search_id = int(search_query)
|
||||
queryset = queryset.filter(id=search_id)
|
||||
except ValueError:
|
||||
queryset = queryset.none()
|
||||
|
||||
# Фильтр по спутнику
|
||||
satellite_ids = self.request.GET.getlist('satellite_id')
|
||||
if satellite_ids:
|
||||
queryset = queryset.filter(id_satellite_id__in=satellite_ids)
|
||||
|
||||
# Фильтр по поляризации
|
||||
polarization_ids = self.request.GET.getlist('polarization_id')
|
||||
if polarization_ids:
|
||||
queryset = queryset.filter(polarization_id__in=polarization_ids)
|
||||
|
||||
# Фильтр по модуляции
|
||||
modulation_ids = self.request.GET.getlist('modulation_id')
|
||||
if modulation_ids:
|
||||
queryset = queryset.filter(modulation_id__in=modulation_ids)
|
||||
|
||||
# Фильтр по стандарту
|
||||
standard_ids = self.request.GET.getlist('standard_id')
|
||||
if standard_ids:
|
||||
queryset = queryset.filter(standard_id__in=standard_ids)
|
||||
|
||||
# Фильтр по частоте
|
||||
freq_min = self.request.GET.get('freq_min', '').strip()
|
||||
freq_max = self.request.GET.get('freq_max', '').strip()
|
||||
if freq_min:
|
||||
try:
|
||||
queryset = queryset.filter(frequency__gte=float(freq_min))
|
||||
except ValueError:
|
||||
pass
|
||||
if freq_max:
|
||||
try:
|
||||
queryset = queryset.filter(frequency__lte=float(freq_max))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Фильтр по символьной скорости
|
||||
sym_min = self.request.GET.get('sym_min', '').strip()
|
||||
sym_max = self.request.GET.get('sym_max', '').strip()
|
||||
if sym_min:
|
||||
try:
|
||||
queryset = queryset.filter(sym_velocity__gte=float(sym_min))
|
||||
except ValueError:
|
||||
pass
|
||||
if sym_max:
|
||||
try:
|
||||
queryset = queryset.filter(sym_velocity__lte=float(sym_max))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Фильтр по дате обновления
|
||||
date_from = self.request.GET.get('date_from', '').strip()
|
||||
date_to = self.request.GET.get('date_to', '').strip()
|
||||
if date_from:
|
||||
queryset = queryset.filter(last_update__gte=date_from)
|
||||
if date_to:
|
||||
queryset = queryset.filter(last_update__lte=date_to)
|
||||
|
||||
# Сортировка
|
||||
sort = self.request.GET.get('sort', '-id')
|
||||
valid_sort_fields = ['id', '-id', 'frequency', '-frequency', 'sym_velocity', '-sym_velocity', 'last_update', '-last_update']
|
||||
if sort in valid_sort_fields:
|
||||
queryset = queryset.order_by(sort)
|
||||
else:
|
||||
queryset = queryset.order_by('-id')
|
||||
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
Добавляет дополнительный контекст для шаблона.
|
||||
"""
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
# Параметры пагинации
|
||||
page_number, items_per_page = parse_pagination_params(self.request, default_per_page=50)
|
||||
context['items_per_page'] = items_per_page
|
||||
context['available_items_per_page'] = [25, 50, 100, 200, 500]
|
||||
|
||||
# Пагинация
|
||||
paginator = Paginator(self.get_queryset(), items_per_page)
|
||||
page_obj = paginator.get_page(page_number)
|
||||
context['page_obj'] = page_obj
|
||||
context['lyngsat_items'] = page_obj.object_list
|
||||
|
||||
# Параметры поиска и фильтрации
|
||||
context['search_query'] = self.request.GET.get('search', '')
|
||||
context['sort'] = self.request.GET.get('sort', '-id')
|
||||
|
||||
# Данные для фильтров - только спутники с существующими записями LyngSat
|
||||
satellites = Satellite.objects.filter(
|
||||
lyngsat__isnull=False
|
||||
).distinct().order_by('name')
|
||||
polarizations = Polarization.objects.all().order_by('name')
|
||||
modulations = Modulation.objects.all().order_by('name')
|
||||
standards = Standard.objects.all().order_by('name')
|
||||
|
||||
# Выбранные фильтры
|
||||
selected_satellites = [int(x) for x in self.request.GET.getlist('satellite_id') if x.isdigit()]
|
||||
selected_polarizations = [int(x) for x in self.request.GET.getlist('polarization_id') if x.isdigit()]
|
||||
selected_modulations = [int(x) for x in self.request.GET.getlist('modulation_id') if x.isdigit()]
|
||||
selected_standards = [int(x) for x in self.request.GET.getlist('standard_id') if x.isdigit()]
|
||||
|
||||
# Параметры фильтров
|
||||
freq_min = self.request.GET.get('freq_min', '')
|
||||
freq_max = self.request.GET.get('freq_max', '')
|
||||
sym_min = self.request.GET.get('sym_min', '')
|
||||
sym_max = self.request.GET.get('sym_max', '')
|
||||
date_from = self.request.GET.get('date_from', '')
|
||||
date_to = self.request.GET.get('date_to', '')
|
||||
|
||||
# Action buttons HTML for toolbar component
|
||||
from django.urls import reverse
|
||||
action_buttons_html = f'''
|
||||
<a href="{reverse('mainapp:fill_lyngsat_data')}" class="btn btn-secondary btn-sm" title="Заполнить данные Lyngsat">
|
||||
<i class="bi bi-cloud-download"></i> Добавить данные
|
||||
</a>
|
||||
<a href="{reverse('mainapp:link_lyngsat')}" class="btn btn-primary btn-sm" title="Привязать источники LyngSat">
|
||||
<i class="bi bi-link-45deg"></i> Привязать
|
||||
</a>
|
||||
<a href="{reverse('mainapp:unlink_all_lyngsat')}" class="btn btn-warning btn-sm" title="Отвязать все источники LyngSat">
|
||||
<i class="bi bi-x-circle"></i> Отвязать
|
||||
</a>
|
||||
'''
|
||||
context['action_buttons_html'] = action_buttons_html
|
||||
|
||||
# Build filter HTML list for filter_panel component
|
||||
filter_html_list = []
|
||||
|
||||
# Satellite filter
|
||||
satellite_options = ''.join([
|
||||
f'<option value="{sat.id}" {"selected" if sat.id in selected_satellites else ""}>{sat.name}</option>'
|
||||
for sat in satellites
|
||||
])
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Спутник:</label>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('satellite_id', true)">Выбрать</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('satellite_id', false)">Снять</button>
|
||||
</div>
|
||||
<select name="satellite_id" class="form-select form-select-sm mb-2" multiple size="6">
|
||||
{satellite_options}
|
||||
</select>
|
||||
</div>
|
||||
''')
|
||||
|
||||
# Polarization filter
|
||||
polarization_options = ''.join([
|
||||
f'<option value="{pol.id}" {"selected" if pol.id in selected_polarizations else ""}>{pol.name}</option>'
|
||||
for pol in polarizations
|
||||
])
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Поляризация:</label>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('polarization_id', true)">Выбрать</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('polarization_id', false)">Снять</button>
|
||||
</div>
|
||||
<select name="polarization_id" class="form-select form-select-sm mb-2" multiple size="4">
|
||||
{polarization_options}
|
||||
</select>
|
||||
</div>
|
||||
''')
|
||||
|
||||
# Modulation filter
|
||||
modulation_options = ''.join([
|
||||
f'<option value="{mod.id}" {"selected" if mod.id in selected_modulations else ""}>{mod.name}</option>'
|
||||
for mod in modulations
|
||||
])
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Модуляция:</label>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('modulation_id', true)">Выбрать</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('modulation_id', false)">Снять</button>
|
||||
</div>
|
||||
<select name="modulation_id" class="form-select form-select-sm mb-2" multiple size="4">
|
||||
{modulation_options}
|
||||
</select>
|
||||
</div>
|
||||
''')
|
||||
|
||||
# Standard filter
|
||||
standard_options = ''.join([
|
||||
f'<option value="{std.id}" {"selected" if std.id in selected_standards else ""}>{std.name}</option>'
|
||||
for std in standards
|
||||
])
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Стандарт:</label>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('standard_id', true)">Выбрать</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('standard_id', false)">Снять</button>
|
||||
</div>
|
||||
<select name="standard_id" class="form-select form-select-sm mb-2" multiple size="4">
|
||||
{standard_options}
|
||||
</select>
|
||||
</div>
|
||||
''')
|
||||
|
||||
# Frequency filter
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Частота, МГц:</label>
|
||||
<input type="number" step="0.001" name="freq_min" class="form-control form-control-sm mb-1"
|
||||
placeholder="От" value="{freq_min}">
|
||||
<input type="number" step="0.001" name="freq_max" class="form-control form-control-sm"
|
||||
placeholder="До" value="{freq_max}">
|
||||
</div>
|
||||
''')
|
||||
|
||||
# Symbol rate filter
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Символьная скорость, БОД:</label>
|
||||
<input type="number" step="0.001" name="sym_min" class="form-control form-control-sm mb-1"
|
||||
placeholder="От" value="{sym_min}">
|
||||
<input type="number" step="0.001" name="sym_max" class="form-control form-control-sm"
|
||||
placeholder="До" value="{sym_max}">
|
||||
</div>
|
||||
''')
|
||||
|
||||
# Date filter
|
||||
filter_html_list.append(f'''
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Дата обновления:</label>
|
||||
<input type="date" name="date_from" id="date_from" class="form-control form-control-sm mb-1"
|
||||
placeholder="От" value="{date_from}">
|
||||
<input type="date" name="date_to" id="date_to" class="form-control form-control-sm"
|
||||
placeholder="До" value="{date_to}">
|
||||
</div>
|
||||
''')
|
||||
|
||||
context['filter_html_list'] = filter_html_list
|
||||
|
||||
# Enable full width layout
|
||||
context['full_width_page'] = True
|
||||
|
||||
return context
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,118 +1,118 @@
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.urls import path
|
||||
from django.views.generic import RedirectView
|
||||
from .views import (
|
||||
ActionsPageView,
|
||||
AddSatellitesView,
|
||||
AddTranspondersView,
|
||||
ClusterTestView,
|
||||
ClearLyngsatCacheView,
|
||||
DeleteSelectedObjectsView,
|
||||
DeleteSelectedSourcesView,
|
||||
DeleteSelectedTranspondersView,
|
||||
DeleteSelectedSatellitesView,
|
||||
FillLyngsatDataView,
|
||||
GeoPointsAPIView,
|
||||
GetLocationsView,
|
||||
HomeView,
|
||||
KubsatView,
|
||||
KubsatExportView,
|
||||
LinkLyngsatSourcesView,
|
||||
LinkVchSigmaView,
|
||||
LoadCsvDataView,
|
||||
LoadExcelDataView,
|
||||
LyngsatDataAPIView,
|
||||
LyngsatTaskStatusAPIView,
|
||||
LyngsatTaskStatusView,
|
||||
ObjItemCreateView,
|
||||
ObjItemDeleteView,
|
||||
ObjItemDetailView,
|
||||
ObjItemListView,
|
||||
ObjItemUpdateView,
|
||||
ProcessKubsatView,
|
||||
SatelliteDataAPIView,
|
||||
SatelliteListView,
|
||||
SatelliteCreateView,
|
||||
SatelliteUpdateView,
|
||||
ShowMapView,
|
||||
ShowSelectedObjectsMapView,
|
||||
ShowSourcesMapView,
|
||||
ShowSourceWithPointsMapView,
|
||||
ShowSourceAveragingStepsMapView,
|
||||
SourceListView,
|
||||
SourceUpdateView,
|
||||
SourceDeleteView,
|
||||
SourceObjItemsAPIView,
|
||||
SigmaParameterDataAPIView,
|
||||
TransponderDataAPIView,
|
||||
TransponderListView,
|
||||
TransponderCreateView,
|
||||
TransponderUpdateView,
|
||||
UnlinkAllLyngsatSourcesView,
|
||||
UploadVchLoadView,
|
||||
custom_logout,
|
||||
)
|
||||
from .views.marks import ObjectMarksListView, AddObjectMarkView, UpdateObjectMarkView
|
||||
|
||||
app_name = 'mainapp'
|
||||
|
||||
urlpatterns = [
|
||||
# Root URL now points to SourceListView (Requirement 1.1)
|
||||
path('', SourceListView.as_view(), name='home'),
|
||||
# Redirect old /home/ URL to source_list for backward compatibility (Requirement 1.2)
|
||||
path('home/', RedirectView.as_view(pattern_name='mainapp:source_list', permanent=True), name='home_redirect'),
|
||||
# Keep /sources/ as an alias (Requirement 1.2)
|
||||
path('sources/', SourceListView.as_view(), name='source_list'),
|
||||
path('source/<int:pk>/edit/', SourceUpdateView.as_view(), name='source_update'),
|
||||
path('source/<int:pk>/delete/', SourceDeleteView.as_view(), name='source_delete'),
|
||||
path('delete-selected-sources/', DeleteSelectedSourcesView.as_view(), name='delete_selected_sources'),
|
||||
path('objitems/', ObjItemListView.as_view(), name='objitem_list'),
|
||||
path('transponders/', TransponderListView.as_view(), name='transponder_list'),
|
||||
path('transponder/create/', TransponderCreateView.as_view(), name='transponder_create'),
|
||||
path('transponder/<int:pk>/edit/', TransponderUpdateView.as_view(), name='transponder_update'),
|
||||
path('delete-selected-transponders/', DeleteSelectedTranspondersView.as_view(), name='delete_selected_transponders'),
|
||||
path('satellites/', SatelliteListView.as_view(), name='satellite_list'),
|
||||
path('satellite/create/', SatelliteCreateView.as_view(), name='satellite_create'),
|
||||
path('satellite/<int:pk>/edit/', SatelliteUpdateView.as_view(), name='satellite_update'),
|
||||
path('delete-selected-satellites/', DeleteSelectedSatellitesView.as_view(), name='delete_selected_satellites'),
|
||||
path('actions/', ActionsPageView.as_view(), name='actions'),
|
||||
path('excel-data', LoadExcelDataView.as_view(), name='load_excel_data'),
|
||||
path('satellites', AddSatellitesView.as_view(), name='add_sats'),
|
||||
path('api/locations/<int:sat_id>/geojson/', GetLocationsView.as_view(), name='locations_by_id'),
|
||||
path('transponders', AddTranspondersView.as_view(), name='add_trans'),
|
||||
path('csv-data', LoadCsvDataView.as_view(), name='load_csv_data'),
|
||||
path('map-points/', ShowMapView.as_view(), name='admin_show_map'),
|
||||
path('show-selected-objects-map/', ShowSelectedObjectsMapView.as_view(), name='show_selected_objects_map'),
|
||||
path('show-sources-map/', ShowSourcesMapView.as_view(), name='show_sources_map'),
|
||||
path('show-source-with-points-map/<int:source_id>/', ShowSourceWithPointsMapView.as_view(), name='show_source_with_points_map'),
|
||||
path('show-source-averaging-map/<int:source_id>/', ShowSourceAveragingStepsMapView.as_view(), name='show_source_averaging_map'),
|
||||
path('delete-selected-objects/', DeleteSelectedObjectsView.as_view(), name='delete_selected_objects'),
|
||||
path('cluster/', ClusterTestView.as_view(), name='cluster'),
|
||||
path('vch-upload/', UploadVchLoadView.as_view(), name='vch_load'),
|
||||
path('vch-link/', LinkVchSigmaView.as_view(), name='link_vch_sigma'),
|
||||
path('link-lyngsat/', LinkLyngsatSourcesView.as_view(), name='link_lyngsat'),
|
||||
path('api/lyngsat/<int:lyngsat_id>/', LyngsatDataAPIView.as_view(), name='lyngsat_data_api'),
|
||||
path('api/sigma-parameter/<int:parameter_id>/', SigmaParameterDataAPIView.as_view(), name='sigma_parameter_data_api'),
|
||||
path('api/source/<int:source_id>/objitems/', SourceObjItemsAPIView.as_view(), name='source_objitems_api'),
|
||||
path('api/transponder/<int:transponder_id>/', TransponderDataAPIView.as_view(), name='transponder_data_api'),
|
||||
path('api/satellite/<int:satellite_id>/', SatelliteDataAPIView.as_view(), name='satellite_data_api'),
|
||||
path('api/geo-points/', GeoPointsAPIView.as_view(), name='geo_points_api'),
|
||||
path('kubsat-excel/', ProcessKubsatView.as_view(), name='kubsat_excel'),
|
||||
path('object/create/', ObjItemCreateView.as_view(), name='objitem_create'),
|
||||
path('object/<int:pk>/edit/', ObjItemUpdateView.as_view(), name='objitem_update'),
|
||||
path('object/<int:pk>/', ObjItemDetailView.as_view(), name='objitem_detail'),
|
||||
path('object/<int:pk>/delete/', ObjItemDeleteView.as_view(), name='objitem_delete'),
|
||||
path('fill-lyngsat-data/', FillLyngsatDataView.as_view(), name='fill_lyngsat_data'),
|
||||
path('lyngsat-task-status/', LyngsatTaskStatusView.as_view(), name='lyngsat_task_status'),
|
||||
path('lyngsat-task-status/<str:task_id>/', LyngsatTaskStatusView.as_view(), name='lyngsat_task_status'),
|
||||
path('api/lyngsat-task-status/<str:task_id>/', LyngsatTaskStatusAPIView.as_view(), name='lyngsat_task_status_api'),
|
||||
path('clear-lyngsat-cache/', ClearLyngsatCacheView.as_view(), name='clear_lyngsat_cache'),
|
||||
path('unlink-all-lyngsat/', UnlinkAllLyngsatSourcesView.as_view(), name='unlink_all_lyngsat'),
|
||||
path('object-marks/', ObjectMarksListView.as_view(), name='object_marks'),
|
||||
path('api/add-object-mark/', AddObjectMarkView.as_view(), name='add_object_mark'),
|
||||
path('api/update-object-mark/', UpdateObjectMarkView.as_view(), name='update_object_mark'),
|
||||
path('kubsat/', KubsatView.as_view(), name='kubsat'),
|
||||
path('kubsat/export/', KubsatExportView.as_view(), name='kubsat_export'),
|
||||
path('logout/', custom_logout, name='logout'),
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.urls import path
|
||||
from django.views.generic import RedirectView
|
||||
from .views import (
|
||||
ActionsPageView,
|
||||
AddSatellitesView,
|
||||
AddTranspondersView,
|
||||
ClusterTestView,
|
||||
ClearLyngsatCacheView,
|
||||
DeleteSelectedObjectsView,
|
||||
DeleteSelectedSourcesView,
|
||||
DeleteSelectedTranspondersView,
|
||||
DeleteSelectedSatellitesView,
|
||||
FillLyngsatDataView,
|
||||
GeoPointsAPIView,
|
||||
GetLocationsView,
|
||||
HomeView,
|
||||
KubsatView,
|
||||
KubsatExportView,
|
||||
LinkLyngsatSourcesView,
|
||||
LinkVchSigmaView,
|
||||
LoadCsvDataView,
|
||||
LoadExcelDataView,
|
||||
LyngsatDataAPIView,
|
||||
LyngsatTaskStatusAPIView,
|
||||
LyngsatTaskStatusView,
|
||||
ObjItemCreateView,
|
||||
ObjItemDeleteView,
|
||||
ObjItemDetailView,
|
||||
ObjItemListView,
|
||||
ObjItemUpdateView,
|
||||
ProcessKubsatView,
|
||||
SatelliteDataAPIView,
|
||||
SatelliteListView,
|
||||
SatelliteCreateView,
|
||||
SatelliteUpdateView,
|
||||
ShowMapView,
|
||||
ShowSelectedObjectsMapView,
|
||||
ShowSourcesMapView,
|
||||
ShowSourceWithPointsMapView,
|
||||
ShowSourceAveragingStepsMapView,
|
||||
SourceListView,
|
||||
SourceUpdateView,
|
||||
SourceDeleteView,
|
||||
SourceObjItemsAPIView,
|
||||
SigmaParameterDataAPIView,
|
||||
TransponderDataAPIView,
|
||||
TransponderListView,
|
||||
TransponderCreateView,
|
||||
TransponderUpdateView,
|
||||
UnlinkAllLyngsatSourcesView,
|
||||
UploadVchLoadView,
|
||||
custom_logout,
|
||||
)
|
||||
from .views.marks import ObjectMarksListView, AddObjectMarkView, UpdateObjectMarkView
|
||||
|
||||
app_name = 'mainapp'
|
||||
|
||||
urlpatterns = [
|
||||
# Root URL now points to SourceListView (Requirement 1.1)
|
||||
path('', SourceListView.as_view(), name='home'),
|
||||
# Redirect old /home/ URL to source_list for backward compatibility (Requirement 1.2)
|
||||
path('home/', RedirectView.as_view(pattern_name='mainapp:source_list', permanent=True), name='home_redirect'),
|
||||
# Keep /sources/ as an alias (Requirement 1.2)
|
||||
path('sources/', SourceListView.as_view(), name='source_list'),
|
||||
path('source/<int:pk>/edit/', SourceUpdateView.as_view(), name='source_update'),
|
||||
path('source/<int:pk>/delete/', SourceDeleteView.as_view(), name='source_delete'),
|
||||
path('delete-selected-sources/', DeleteSelectedSourcesView.as_view(), name='delete_selected_sources'),
|
||||
path('objitems/', ObjItemListView.as_view(), name='objitem_list'),
|
||||
path('transponders/', TransponderListView.as_view(), name='transponder_list'),
|
||||
path('transponder/create/', TransponderCreateView.as_view(), name='transponder_create'),
|
||||
path('transponder/<int:pk>/edit/', TransponderUpdateView.as_view(), name='transponder_update'),
|
||||
path('delete-selected-transponders/', DeleteSelectedTranspondersView.as_view(), name='delete_selected_transponders'),
|
||||
path('satellites/', SatelliteListView.as_view(), name='satellite_list'),
|
||||
path('satellite/create/', SatelliteCreateView.as_view(), name='satellite_create'),
|
||||
path('satellite/<int:pk>/edit/', SatelliteUpdateView.as_view(), name='satellite_update'),
|
||||
path('delete-selected-satellites/', DeleteSelectedSatellitesView.as_view(), name='delete_selected_satellites'),
|
||||
path('actions/', ActionsPageView.as_view(), name='actions'),
|
||||
path('excel-data', LoadExcelDataView.as_view(), name='load_excel_data'),
|
||||
path('satellites', AddSatellitesView.as_view(), name='add_sats'),
|
||||
path('api/locations/<int:sat_id>/geojson/', GetLocationsView.as_view(), name='locations_by_id'),
|
||||
path('transponders', AddTranspondersView.as_view(), name='add_trans'),
|
||||
path('csv-data', LoadCsvDataView.as_view(), name='load_csv_data'),
|
||||
path('map-points/', ShowMapView.as_view(), name='admin_show_map'),
|
||||
path('show-selected-objects-map/', ShowSelectedObjectsMapView.as_view(), name='show_selected_objects_map'),
|
||||
path('show-sources-map/', ShowSourcesMapView.as_view(), name='show_sources_map'),
|
||||
path('show-source-with-points-map/<int:source_id>/', ShowSourceWithPointsMapView.as_view(), name='show_source_with_points_map'),
|
||||
path('show-source-averaging-map/<int:source_id>/', ShowSourceAveragingStepsMapView.as_view(), name='show_source_averaging_map'),
|
||||
path('delete-selected-objects/', DeleteSelectedObjectsView.as_view(), name='delete_selected_objects'),
|
||||
path('cluster/', ClusterTestView.as_view(), name='cluster'),
|
||||
path('vch-upload/', UploadVchLoadView.as_view(), name='vch_load'),
|
||||
path('vch-link/', LinkVchSigmaView.as_view(), name='link_vch_sigma'),
|
||||
path('link-lyngsat/', LinkLyngsatSourcesView.as_view(), name='link_lyngsat'),
|
||||
path('api/lyngsat/<int:lyngsat_id>/', LyngsatDataAPIView.as_view(), name='lyngsat_data_api'),
|
||||
path('api/sigma-parameter/<int:parameter_id>/', SigmaParameterDataAPIView.as_view(), name='sigma_parameter_data_api'),
|
||||
path('api/source/<int:source_id>/objitems/', SourceObjItemsAPIView.as_view(), name='source_objitems_api'),
|
||||
path('api/transponder/<int:transponder_id>/', TransponderDataAPIView.as_view(), name='transponder_data_api'),
|
||||
path('api/satellite/<int:satellite_id>/', SatelliteDataAPIView.as_view(), name='satellite_data_api'),
|
||||
path('api/geo-points/', GeoPointsAPIView.as_view(), name='geo_points_api'),
|
||||
path('kubsat-excel/', ProcessKubsatView.as_view(), name='kubsat_excel'),
|
||||
path('object/create/', ObjItemCreateView.as_view(), name='objitem_create'),
|
||||
path('object/<int:pk>/edit/', ObjItemUpdateView.as_view(), name='objitem_update'),
|
||||
path('object/<int:pk>/', ObjItemDetailView.as_view(), name='objitem_detail'),
|
||||
path('object/<int:pk>/delete/', ObjItemDeleteView.as_view(), name='objitem_delete'),
|
||||
path('fill-lyngsat-data/', FillLyngsatDataView.as_view(), name='fill_lyngsat_data'),
|
||||
path('lyngsat-task-status/', LyngsatTaskStatusView.as_view(), name='lyngsat_task_status'),
|
||||
path('lyngsat-task-status/<str:task_id>/', LyngsatTaskStatusView.as_view(), name='lyngsat_task_status'),
|
||||
path('api/lyngsat-task-status/<str:task_id>/', LyngsatTaskStatusAPIView.as_view(), name='lyngsat_task_status_api'),
|
||||
path('clear-lyngsat-cache/', ClearLyngsatCacheView.as_view(), name='clear_lyngsat_cache'),
|
||||
path('unlink-all-lyngsat/', UnlinkAllLyngsatSourcesView.as_view(), name='unlink_all_lyngsat'),
|
||||
path('object-marks/', ObjectMarksListView.as_view(), name='object_marks'),
|
||||
path('api/add-object-mark/', AddObjectMarkView.as_view(), name='add_object_mark'),
|
||||
path('api/update-object-mark/', UpdateObjectMarkView.as_view(), name='update_object_mark'),
|
||||
path('kubsat/', KubsatView.as_view(), name='kubsat'),
|
||||
path('kubsat/export/', KubsatExportView.as_view(), name='kubsat_export'),
|
||||
path('logout/', custom_logout, name='logout'),
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,6 @@ dependencies = [
|
||||
"django-dynamic-raw-id>=4.4",
|
||||
"django-import-export>=4.3.10",
|
||||
"django-leaflet>=0.32.0",
|
||||
"django-map-widgets>=0.5.1",
|
||||
"django-more-admin-filters>=1.13",
|
||||
"dotenv>=0.9.9",
|
||||
"flower>=2.0.1",
|
||||
|
||||
11
dbapp/uv.lock
generated
11
dbapp/uv.lock
generated
@@ -366,7 +366,6 @@ dependencies = [
|
||||
{ name = "django-dynamic-raw-id" },
|
||||
{ name = "django-import-export" },
|
||||
{ name = "django-leaflet" },
|
||||
{ name = "django-map-widgets" },
|
||||
{ name = "django-more-admin-filters" },
|
||||
{ name = "django-redis" },
|
||||
{ name = "dotenv" },
|
||||
@@ -407,7 +406,6 @@ requires-dist = [
|
||||
{ name = "django-dynamic-raw-id", specifier = ">=4.4" },
|
||||
{ name = "django-import-export", specifier = ">=4.3.10" },
|
||||
{ name = "django-leaflet", specifier = ">=0.32.0" },
|
||||
{ name = "django-map-widgets", specifier = ">=0.5.1" },
|
||||
{ name = "django-more-admin-filters", specifier = ">=1.13" },
|
||||
{ name = "django-redis", specifier = ">=5.4.0" },
|
||||
{ name = "dotenv", specifier = ">=0.9.9" },
|
||||
@@ -598,15 +596,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/d3/bf4a46eff75a5a804fc32588696d2dcd04370008041114009f0f35a3fb42/django_leaflet-0.32.0-py3-none-any.whl", hash = "sha256:a17d8e6cc05dd98e8e543fbf198b81dabbf9f195c222e786d1686aeda91c1aa8", size = 582439, upload-time = "2025-05-14T12:49:34.151Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-map-widgets"
|
||||
version = "0.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/78/50/651dae7335fc9c6df7b1ab27c49b1cc98245ac0d61750538a192da19e671/django_map_widgets-0.5.1.tar.gz", hash = "sha256:68e81f9c58c1cd6d180421220a4d100a185c8062ae0ca7be790658fcfd4eda1d", size = 160819, upload-time = "2024-07-09T17:37:50.717Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/75/7f1782c9fa3c07c2ca63ce7b65c4838afb568a5ea71aa119aaa9dc456d8b/django_map_widgets-0.5.1-py3-none-any.whl", hash = "sha256:7307935163b46c6a2a225c85c91c7262a8b47a5c3aefbbc6d8fc7a5fda53b7cd", size = 256008, upload-time = "2024-07-09T17:37:48.941Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-more-admin-filters"
|
||||
version = "1.13"
|
||||
|
||||
Reference in New Issue
Block a user