rework main models
This commit is contained in:
@@ -18,34 +18,26 @@ RUN apt-get update && apt-get install -y \
|
|||||||
postgresql-client \
|
postgresql-client \
|
||||||
build-essential \
|
build-essential \
|
||||||
libpq-dev \
|
libpq-dev \
|
||||||
gcc \
|
|
||||||
g++ \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Install Python dependencies for GDAL
|
|
||||||
RUN pip install --upgrade pip && \
|
|
||||||
pip install --no-cache-dir GDAL==$(gdal-config --version)
|
|
||||||
|
|
||||||
# Set work directory
|
# Set work directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy project requirements
|
# Copy project files
|
||||||
COPY pyproject.toml uv.lock ./
|
COPY pyproject.toml uv.lock ./
|
||||||
|
|
||||||
# Install uv package manager
|
# Install uv and dependencies
|
||||||
RUN pip install --upgrade pip && pip install uv
|
RUN pip install --no-cache-dir uv && \
|
||||||
|
uv sync --frozen --no-dev
|
||||||
|
|
||||||
# Install dependencies using uv
|
# Copy project code (после установки зависимостей для лучшего кэширования)
|
||||||
RUN uv pip install --system --no-cache-dir -r uv.lock
|
|
||||||
|
|
||||||
# Copy project
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Collect static files
|
# Collect static files
|
||||||
RUN python manage.py collectstatic --noinput
|
RUN uv run manage.py collectstatic --noinput
|
||||||
|
|
||||||
# Expose port
|
# Expose port
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
# Run gunicorn server
|
# Run gunicorn server
|
||||||
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "3", "dbapp.wsgi:application"]
|
CMD [".venv/bin/gunicorn", "--bind", "0.0.0.0:8000", "--workers", "3", "dbapp.wsgi:application"]
|
||||||
@@ -72,6 +72,7 @@ INSTALLED_APPS = [
|
|||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
"debug_toolbar.middleware.DebugToolbarMiddleware", #Добавил
|
"debug_toolbar.middleware.DebugToolbarMiddleware", #Добавил
|
||||||
|
'django.middleware.locale.LocaleMiddleware', #Добавил
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware', #Добавил
|
'django.contrib.messages.middleware.MessageMiddleware', #Добавил
|
||||||
|
|||||||
BIN
dbapp/gdal-3.10.2-cp313-cp313-win_amd64.whl
Normal file
BIN
dbapp/gdal-3.10.2-cp313-cp313-win_amd64.whl
Normal file
Binary file not shown.
@@ -6,6 +6,7 @@ from .models import (
|
|||||||
Standard,
|
Standard,
|
||||||
SigmaParMark,
|
SigmaParMark,
|
||||||
SigmaParameter,
|
SigmaParameter,
|
||||||
|
SourceType,
|
||||||
Parameter,
|
Parameter,
|
||||||
Satellite,
|
Satellite,
|
||||||
Mirror,
|
Mirror,
|
||||||
@@ -128,6 +129,12 @@ class ModulationAdmin(admin.ModelAdmin):
|
|||||||
search_fields = ("name",)
|
search_fields = ("name",)
|
||||||
ordering = ("name",)
|
ordering = ("name",)
|
||||||
|
|
||||||
|
@admin.register(SourceType)
|
||||||
|
class ModulationAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ("name",)
|
||||||
|
search_fields = ("name",)
|
||||||
|
ordering = ("name",)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Standard)
|
@admin.register(Standard)
|
||||||
class StandardAdmin(admin.ModelAdmin):
|
class StandardAdmin(admin.ModelAdmin):
|
||||||
@@ -209,23 +216,25 @@ class ParameterAdmin(ImportExportActionModelAdmin, admin.ModelAdmin):
|
|||||||
class SigmaParameterAdmin(ImportExportActionModelAdmin, admin.ModelAdmin):
|
class SigmaParameterAdmin(ImportExportActionModelAdmin, admin.ModelAdmin):
|
||||||
list_display = (
|
list_display = (
|
||||||
"id_satellite",
|
"id_satellite",
|
||||||
"status",
|
# "status",
|
||||||
"frequency",
|
"frequency",
|
||||||
|
"transfer_frequency",
|
||||||
"freq_range",
|
"freq_range",
|
||||||
"power",
|
# "power",
|
||||||
|
"polarization",
|
||||||
"modulation",
|
"modulation",
|
||||||
"bod_velocity",
|
"bod_velocity",
|
||||||
"snr",
|
"snr",
|
||||||
"standard",
|
# "standard",
|
||||||
"parameter",
|
"parameter",
|
||||||
"packets",
|
# "packets",
|
||||||
"datetime_begin",
|
"datetime_begin",
|
||||||
"datetime_end",
|
"datetime_end",
|
||||||
)
|
)
|
||||||
readonly_fields = (
|
readonly_fields = (
|
||||||
"datetime_begin",
|
"datetime_begin",
|
||||||
"datetime_end",
|
"datetime_end",
|
||||||
|
"transfer_frequency"
|
||||||
)
|
)
|
||||||
list_display_links = ("id_satellite",)
|
list_display_links = ("id_satellite",)
|
||||||
list_filter = (
|
list_filter = (
|
||||||
@@ -401,7 +410,7 @@ class ObjectAdmin(admin.ModelAdmin):
|
|||||||
)
|
)
|
||||||
search_fields = (
|
search_fields = (
|
||||||
"name",
|
"name",
|
||||||
# "id_geo",
|
"id_geo__coords",
|
||||||
# "id_satellite__name",
|
# "id_satellite__name",
|
||||||
# "id_vch_load__frequency",
|
# "id_vch_load__frequency",
|
||||||
)
|
)
|
||||||
@@ -413,6 +422,7 @@ class ObjectAdmin(admin.ModelAdmin):
|
|||||||
"id_vch_load__modulation",
|
"id_vch_load__modulation",
|
||||||
"id_vch_load__id_satellite",
|
"id_vch_load__id_satellite",
|
||||||
"id_geo",
|
"id_geo",
|
||||||
|
"id_source_type"
|
||||||
)
|
)
|
||||||
autocomplete_fields = ("id_geo",)
|
autocomplete_fields = ("id_geo",)
|
||||||
raw_id_fields = ("id_vch_load",)
|
raw_id_fields = ("id_vch_load",)
|
||||||
@@ -422,11 +432,13 @@ class ObjectAdmin(admin.ModelAdmin):
|
|||||||
def sat_name(self, obj):
|
def sat_name(self, obj):
|
||||||
return obj.id_vch_load.id_satellite
|
return obj.id_vch_load.id_satellite
|
||||||
sat_name.short_description = "Спутник"
|
sat_name.short_description = "Спутник"
|
||||||
|
sat_name.admin_order_field = "id_vch_load__id_satellite__name"
|
||||||
|
|
||||||
def freq(self, obj):
|
def freq(self, obj):
|
||||||
par = obj.id_vch_load
|
par = obj.id_vch_load
|
||||||
return par.frequency
|
return par.frequency
|
||||||
freq.short_description = "Частота, МГц"
|
freq.short_description = "Частота, МГц"
|
||||||
|
freq.admin_order_field = "id_vch_load__frequency"
|
||||||
|
|
||||||
def distance_geo_kup(self, obj):
|
def distance_geo_kup(self, obj):
|
||||||
par = obj.id_geo.distance_coords_kup
|
par = obj.id_geo.distance_coords_kup
|
||||||
@@ -458,6 +470,7 @@ class ObjectAdmin(admin.ModelAdmin):
|
|||||||
par = obj.id_vch_load
|
par = obj.id_vch_load
|
||||||
return par.freq_range
|
return par.freq_range
|
||||||
freq_range.short_description = "Полоса, МГц"
|
freq_range.short_description = "Полоса, МГц"
|
||||||
|
freq_range.admin_order_field = "id_vch_load__freq_range"
|
||||||
|
|
||||||
def bod_velocity(self, obj):
|
def bod_velocity(self, obj):
|
||||||
par = obj.id_vch_load
|
par = obj.id_vch_load
|
||||||
@@ -482,6 +495,7 @@ class ObjectAdmin(admin.ModelAdmin):
|
|||||||
lat = f"{latitude}N" if latitude > 0 else f"{abs(latitude)}S"
|
lat = f"{latitude}N" if latitude > 0 else f"{abs(latitude)}S"
|
||||||
return f"{lat} {lon}"
|
return f"{lat} {lon}"
|
||||||
geo_coords.short_description = "Координаты геолокации"
|
geo_coords.short_description = "Координаты геолокации"
|
||||||
|
geo_coords.admin_order_filed = "id_geo__coords"
|
||||||
|
|
||||||
def kupsat_coords(self, obj):
|
def kupsat_coords(self, obj):
|
||||||
obj = obj.id_geo
|
obj = obj.id_geo
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from .models import Satellite
|
from .models import Satellite, Polarization
|
||||||
|
|
||||||
|
class UploadFileForm(forms.Form):
|
||||||
|
file = forms.FileField(
|
||||||
|
label="Выберите файл",
|
||||||
|
widget=forms.FileInput(attrs={
|
||||||
|
'class': 'form-file-input'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LoadExcelData(forms.Form):
|
class LoadExcelData(forms.Form):
|
||||||
file = forms.FileField(
|
file = forms.FileField(
|
||||||
@@ -33,7 +42,7 @@ class LoadCsvData(forms.Form):
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
class UploadFileForm(forms.Form):
|
class UploadVchLoad(UploadFileForm):
|
||||||
sat_choice = forms.ModelChoiceField(
|
sat_choice = forms.ModelChoiceField(
|
||||||
queryset=Satellite.objects.all(),
|
queryset=Satellite.objects.all(),
|
||||||
label="Выберите спутник",
|
label="Выберите спутник",
|
||||||
@@ -41,12 +50,7 @@ class UploadFileForm(forms.Form):
|
|||||||
'class': 'form-select'
|
'class': 'form-select'
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
file = forms.FileField(
|
|
||||||
label="Выберите текстовый файл",
|
|
||||||
widget=forms.FileInput(attrs={
|
|
||||||
'class': 'form-file-input'
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
class VchLinkForm(forms.Form):
|
class VchLinkForm(forms.Form):
|
||||||
sat_choice = forms.ModelChoiceField(
|
sat_choice = forms.ModelChoiceField(
|
||||||
@@ -75,4 +79,28 @@ class VchLinkForm(forms.Form):
|
|||||||
'class': 'form-control',
|
'class': 'form-control',
|
||||||
'placeholder': 'Введите второе число'
|
'placeholder': 'Введите второе число'
|
||||||
})
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NewEventForm(forms.Form):
|
||||||
|
# sat_choice = forms.ModelChoiceField(
|
||||||
|
# queryset=Satellite.objects.all(),
|
||||||
|
# label="Выберите спутник",
|
||||||
|
# widget=forms.Select(attrs={
|
||||||
|
# 'class': 'form-select'
|
||||||
|
# })
|
||||||
|
# )
|
||||||
|
# pol_choice = forms.ModelChoiceField(
|
||||||
|
# queryset=Polarization.objects.all(),
|
||||||
|
# label="Выберите поляризацию",
|
||||||
|
# widget=forms.Select(attrs={
|
||||||
|
# 'class': 'form-select'
|
||||||
|
# })
|
||||||
|
# )
|
||||||
|
file = forms.FileField(
|
||||||
|
label="Выберите файл",
|
||||||
|
widget=forms.FileInput(attrs={
|
||||||
|
'class': 'form-control',
|
||||||
|
'accept': '.xlsx,.xls'
|
||||||
|
})
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-10-27 13:10
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.db.models.expressions
|
||||||
|
import mainapp.models
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mainapp', '0017_alter_sigmaparameter_parameter'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='sigmaparameter',
|
||||||
|
name='polarization',
|
||||||
|
field=models.ForeignKey(blank=True, default=mainapp.models.get_default_polarization, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='polarizations_sigma', to='mainapp.polarization', verbose_name='Поляризация'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='sigmaparameter',
|
||||||
|
name='transfer',
|
||||||
|
field=models.FloatField(choices=[(-1.0, '-'), (9750.0, '9750 МГц'), (10750.0, '10750 МГц')], default=-1.0, verbose_name='Перенос по частоте'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='sigmaparameter',
|
||||||
|
name='transfer_frequency',
|
||||||
|
field=models.GeneratedField(db_persist=True, expression=models.ExpressionWrapper(django.db.models.expressions.CombinedExpression(models.F('frequency'), '+', models.F('transfer')), output_field=models.FloatField()), null=True, output_field=models.FloatField(), verbose_name='Частота в Ku, МГц'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
dbapp/mainapp/migrations/0019_alter_satellite_name.py
Normal file
18
dbapp/mainapp/migrations/0019_alter_satellite_name.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-10-28 05:41
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mainapp', '0018_sigmaparameter_polarization_sigmaparameter_transfer_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='satellite',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(db_index=True, max_length=100, unique=True, verbose_name='Имя спутника'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-10-29 14:00
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mainapp', '0019_alter_satellite_name'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SourceType',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=50, unique=True, verbose_name='Тип источника')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Тип источника',
|
||||||
|
'verbose_name_plural': 'Типы источников',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='objitem',
|
||||||
|
name='id_source_type',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='objitems', to='mainapp.sourcetype', verbose_name='Тип источника'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -2,6 +2,7 @@ from django.db import models
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.gis.db import models as gis
|
from django.contrib.gis.db import models as gis
|
||||||
from django.contrib.gis.db.models import functions
|
from django.contrib.gis.db.models import functions
|
||||||
|
from django.db.models import F, ExpressionWrapper
|
||||||
|
|
||||||
def get_default_polarization():
|
def get_default_polarization():
|
||||||
obj, created = Polarization.objects.get_or_create(
|
obj, created = Polarization.objects.get_or_create(
|
||||||
@@ -96,7 +97,7 @@ class Standard(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Satellite(models.Model):
|
class Satellite(models.Model):
|
||||||
name = models.CharField(max_length=30, unique=True, verbose_name="Имя спутника", db_index=True)
|
name = models.CharField(max_length=100, unique=True, verbose_name="Имя спутника", db_index=True)
|
||||||
norad = models.IntegerField(blank=True, null=True, verbose_name="NORAD ID")
|
norad = models.IntegerField(blank=True, null=True, verbose_name="NORAD ID")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -107,6 +108,40 @@ class Satellite(models.Model):
|
|||||||
verbose_name_plural = "Спутники"
|
verbose_name_plural = "Спутники"
|
||||||
|
|
||||||
|
|
||||||
|
class ObjItem(models.Model):
|
||||||
|
name = models.CharField(null=True, blank=True, max_length=100, verbose_name="Имя объекта", db_index=True)
|
||||||
|
# id_satellite = models.ForeignKey(Satellite, on_delete=models.PROTECT, related_name="objitems", verbose_name="Спутник")
|
||||||
|
# id_vch_load = models.ForeignKey(Parameter, on_delete=models.CASCADE, related_name="objitems", verbose_name="ВЧ загрузка")
|
||||||
|
# id_geo = models.ForeignKey(Geo, on_delete=models.CASCADE, related_name="objitems", verbose_name="Геоданные")
|
||||||
|
id_user_add = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name="objitems", verbose_name="Пользователь", null=True, blank=True)
|
||||||
|
# id_source_type = models.ForeignKey(SourceType, on_delete=models.SET_NULL, related_name="objitems", verbose_name='Тип источника', null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Объект {self.name}"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Объект"
|
||||||
|
verbose_name_plural = "Объекты"
|
||||||
|
# constraints = [
|
||||||
|
# models.UniqueConstraint(
|
||||||
|
# fields=['id_vch_load', 'id_geo'],
|
||||||
|
# name='unique_objitem_combination'
|
||||||
|
# )
|
||||||
|
# ]
|
||||||
|
|
||||||
|
class SourceType(models.Model):
|
||||||
|
name = models.CharField(max_length=50, unique=True, verbose_name="Тип источника")
|
||||||
|
objitem = models.OneToOneField(ObjItem, on_delete=models.SET_NULL, verbose_name="Гео", related_name="objitems", null=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Тип источника"
|
||||||
|
verbose_name_plural = 'Типы источников'
|
||||||
|
|
||||||
|
|
||||||
class Parameter(models.Model):
|
class Parameter(models.Model):
|
||||||
id_satellite = models.ForeignKey(Satellite, on_delete=models.PROTECT, related_name="parameters", verbose_name="Спутник", null=True)
|
id_satellite = models.ForeignKey(Satellite, on_delete=models.PROTECT, related_name="parameters", verbose_name="Спутник", null=True)
|
||||||
polarization = models.ForeignKey(
|
polarization = models.ForeignKey(
|
||||||
@@ -123,6 +158,7 @@ class Parameter(models.Model):
|
|||||||
Standard, default=get_default_standard, on_delete=models.SET_DEFAULT, related_name="standards", null=True, blank=True, verbose_name="Стандарт"
|
Standard, default=get_default_standard, on_delete=models.SET_DEFAULT, related_name="standards", null=True, blank=True, verbose_name="Стандарт"
|
||||||
)
|
)
|
||||||
id_user_add = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name="parameter_added", verbose_name="Пользователь", null=True, blank=True)
|
id_user_add = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name="parameter_added", verbose_name="Пользователь", null=True, blank=True)
|
||||||
|
objitem = models.ForeignKey(ObjItem, on_delete=models.SET_NULL, related_name="objitems", verbose_name="Источник",null=True, blank=True)
|
||||||
# id_sigma_parameter = models.ManyToManyField(SigmaParameter, on_delete=models.SET_NULL, related_name="sigma_parameter", verbose_name="ВЧ с sigma", null=True, blank=True)
|
# id_sigma_parameter = models.ManyToManyField(SigmaParameter, on_delete=models.SET_NULL, related_name="sigma_parameter", verbose_name="ВЧ с sigma", null=True, blank=True)
|
||||||
# id_sigma_parameter = models.ManyToManyField(SigmaParameter, verbose_name="ВЧ с sigma", null=True, blank=True)
|
# id_sigma_parameter = models.ManyToManyField(SigmaParameter, verbose_name="ВЧ с sigma", null=True, blank=True)
|
||||||
|
|
||||||
@@ -151,12 +187,35 @@ class Parameter(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class SigmaParameter(models.Model):
|
class SigmaParameter(models.Model):
|
||||||
|
TRANSFERS = [
|
||||||
|
(-1.0, "-"),
|
||||||
|
(9750.0, "9750 МГц"),
|
||||||
|
(10750.0, "10750 МГц")
|
||||||
|
]
|
||||||
|
|
||||||
id_satellite = models.ForeignKey(Satellite, on_delete=models.PROTECT, related_name="sigmapar_sat", verbose_name="Спутник")
|
id_satellite = models.ForeignKey(Satellite, on_delete=models.PROTECT, related_name="sigmapar_sat", verbose_name="Спутник")
|
||||||
|
transfer = models.FloatField(
|
||||||
|
choices=TRANSFERS,
|
||||||
|
default=-1.0,
|
||||||
|
verbose_name="Перенос по частоте"
|
||||||
|
)
|
||||||
status = models.CharField(max_length=20, blank=True, null=True, verbose_name="Статус")
|
status = models.CharField(max_length=20, blank=True, null=True, verbose_name="Статус")
|
||||||
frequency = models.FloatField(default=0, null=True, blank=True, verbose_name="Частота, МГц", db_index=True)
|
frequency = models.FloatField(default=0, null=True, blank=True, verbose_name="Частота, МГц", db_index=True)
|
||||||
|
transfer_frequency = models.GeneratedField(
|
||||||
|
expression=ExpressionWrapper(
|
||||||
|
F('frequency') + F('transfer'),
|
||||||
|
output_field=models.FloatField()
|
||||||
|
),
|
||||||
|
output_field=models.FloatField(),
|
||||||
|
db_persist=True,
|
||||||
|
null=True, blank=True, verbose_name="Частота в Ku, МГц"
|
||||||
|
)
|
||||||
freq_range = models.FloatField(default=0, null=True, blank=True, verbose_name="Полоса частот, МГц")
|
freq_range = models.FloatField(default=0, null=True, blank=True, verbose_name="Полоса частот, МГц")
|
||||||
power = models.FloatField(default=0, null=True, blank=True, verbose_name="Мощность, дБм")
|
power = models.FloatField(default=0, null=True, blank=True, verbose_name="Мощность, дБм")
|
||||||
bod_velocity = models.FloatField(default=0, null=True, blank=True, verbose_name="Символьная скорость, БОД")
|
bod_velocity = models.FloatField(default=0, null=True, blank=True, verbose_name="Символьная скорость, БОД")
|
||||||
|
polarization = models.ForeignKey(
|
||||||
|
Polarization, default=get_default_polarization, on_delete=models.SET_DEFAULT, related_name="polarizations_sigma", null=True, blank=True, verbose_name="Поляризация"
|
||||||
|
)
|
||||||
modulation = models.ForeignKey(
|
modulation = models.ForeignKey(
|
||||||
Modulation, default=get_default_modulation, on_delete=models.SET_DEFAULT, related_name="modulations_sigma", null=True, blank=True, verbose_name="Модуляция"
|
Modulation, default=get_default_modulation, on_delete=models.SET_DEFAULT, related_name="modulations_sigma", null=True, blank=True, verbose_name="Модуляция"
|
||||||
)
|
)
|
||||||
@@ -213,6 +272,7 @@ class Geo(models.Model):
|
|||||||
db_persist=True,
|
db_persist=True,
|
||||||
null=True, blank=True, verbose_name="Расстояние между купсатом и оперативным отделом, км"
|
null=True, blank=True, verbose_name="Расстояние между купсатом и оперативным отделом, км"
|
||||||
)
|
)
|
||||||
|
objitem = models.OneToOneField(ObjItem, on_delete=models.SET_NULL, verbose_name="Гео", related_name="objitems", null=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
longitude = self.coords.coords[0]
|
longitude = self.coords.coords[0]
|
||||||
@@ -234,23 +294,3 @@ class Geo(models.Model):
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ObjItem(models.Model):
|
|
||||||
name = models.CharField(null=True, blank=True, max_length=100, verbose_name="Имя объекта", db_index=True)
|
|
||||||
# id_satellite = models.ForeignKey(Satellite, on_delete=models.PROTECT, related_name="objitems", verbose_name="Спутник")
|
|
||||||
id_vch_load = models.ForeignKey(Parameter, on_delete=models.CASCADE, related_name="objitems", verbose_name="ВЧ загрузка")
|
|
||||||
id_geo = models.ForeignKey(Geo, on_delete=models.CASCADE, related_name="objitems", verbose_name="Геоданные")
|
|
||||||
id_user_add = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name="objitems", verbose_name="Пользователь", null=True, blank=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"Объект {self.name}"
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = "Объект"
|
|
||||||
verbose_name_plural = "Объекты"
|
|
||||||
constraints = [
|
|
||||||
models.UniqueConstraint(
|
|
||||||
fields=['id_vch_load', 'id_geo'],
|
|
||||||
name='unique_objitem_combination'
|
|
||||||
)
|
|
||||||
]
|
|
||||||
@@ -171,6 +171,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- New Event Card -->
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="card h-100 shadow-sm border-0">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex align-items-center mb-3">
|
||||||
|
<div class="bg-success bg-opacity-10 rounded-circle p-2 me-3">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-plus-circle text-success" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0M4.5 7.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5M7.5 4.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 0 1h-1a.5.5 0 0 1-.5-.5m1 3a.5.5 0 0 1 .5.5v3a.5.5 0 0 1-1 0v-3a.5.5 0 0 1 .5-.5"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="card-title mb-0">Формирование таблицы для Кубсатов</h3>
|
||||||
|
</div>
|
||||||
|
<p class="card-text">Добавьте новое событие с помощью выбора спутника и загрузки файла данных.</p>
|
||||||
|
<a href="{% url 'kubsat_excel' %}" class="btn btn-success">
|
||||||
|
Добавить событие
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -31,13 +31,13 @@
|
|||||||
<div class="text-danger mt-1">{{ form.sat_choice.errors }}</div>
|
<div class="text-danger mt-1">{{ form.sat_choice.errors }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
{% comment %} <div class="mb-3">
|
||||||
<label for="{{ form.ku_range.id_for_label }}" class="form-label">Выберите перенос по частоте(МГц):</label>
|
<label for="{{ form.ku_range.id_for_label }}" class="form-label">Выберите перенос по частоте(МГц):</label>
|
||||||
{{ form.ku_range }}
|
{{ form.ku_range }}
|
||||||
{% if form.ku_range.errors %}
|
{% if form.ku_range.errors %}
|
||||||
<div class="text-danger mt-1">{{ form.ku_range.errors }}</div>
|
<div class="text-danger mt-1">{{ form.ku_range.errors }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div> {% endcomment %}
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="{{ form.value1.id_for_label }}" class="form-label">Разброс по частоте(в %)</label>
|
<label for="{{ form.value1.id_for_label }}" class="form-label">Разброс по частоте(в %)</label>
|
||||||
{{ form.value1 }}
|
{{ form.value1 }}
|
||||||
|
|||||||
52
dbapp/mainapp/templates/mainapp/process_kubsat.html
Normal file
52
dbapp/mainapp/templates/mainapp/process_kubsat.html
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{% extends 'mainapp/base.html' %}
|
||||||
|
|
||||||
|
{% block title %}Новое событие{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card shadow-sm border-0">
|
||||||
|
<div class="card-header bg-success text-white">
|
||||||
|
<h2 class="mb-0">Формирование таблицы Кубсат</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% comment%}
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="{{ form.sat_choice.id_for_label }}" class="form-label">{{ form.sat_choice.label }}</label>
|
||||||
|
{{ form.sat_choice }}
|
||||||
|
{% if form.sat_choice.errors %}
|
||||||
|
<div class="text-danger mt-1">{{ form.sat_choice.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>{% endcomment %}
|
||||||
|
|
||||||
|
{% comment %} <div class="mb-4">
|
||||||
|
<label for="{{ form.pol_choice.id_for_label }}" class="form-label">{{ form.pol_choice.label }}</label>
|
||||||
|
{{ form.pol_choice }}
|
||||||
|
{% if form.pol_choice.errors %}
|
||||||
|
<div class="text-danger mt-1">{{ form.pol_choice.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div> {% endcomment %}
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="{{ form.file.id_for_label }}" class="form-label">{{ form.file.label }}</label>
|
||||||
|
{{ form.file }}
|
||||||
|
{% if form.file.errors %}
|
||||||
|
<div class="text-danger mt-1">{{ form.file.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-text">Выберите файл для загрузки</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<a href="{% url 'home' %}" class="btn btn-secondary">Назад</a>
|
||||||
|
<button type="submit" class="btn btn-success">Выполнить</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
53
dbapp/mainapp/templates/mainapp/transponders_upload.html
Normal file
53
dbapp/mainapp/templates/mainapp/transponders_upload.html
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
{% extends 'mainapp/base.html' %}
|
||||||
|
|
||||||
|
{% block title %}Загрузка данных транспондеров{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header bg-warning text-white">
|
||||||
|
<h2 class="mb-0">Загрузка данных транспондеров из CellNet</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% if messages %}
|
||||||
|
{% for message in messages %}
|
||||||
|
<div class="alert {{ message.tags }} alert-dismissible fade show" role="alert">
|
||||||
|
{{ message }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<p class="card-text">Загрузите xml-файл и выберите спутник для загрузки данных в базу.</p>
|
||||||
|
|
||||||
|
<form method="post" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="{{ form.file.id_for_label }}" class="form-label">Выберите xml файл:</label>
|
||||||
|
{{ form.file }}
|
||||||
|
{% if form.file.errors %}
|
||||||
|
<div class="text-danger mt-1">{{ form.file.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-text">Загрузите xml-файл (.xml) с данными для обработки</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% comment %} <div class="mb-3">
|
||||||
|
<label for="{{ form.sat_choice.id_for_label }}" class="form-label">Выберите спутник:</label>
|
||||||
|
{{ form.sat_choice }}
|
||||||
|
{% if form.sat_choice.errors %}
|
||||||
|
<div class="text-danger mt-1">{{ form.sat_choice.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div> {% endcomment %}
|
||||||
|
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||||
|
<a href="{% url 'home' %}" class="btn btn-secondary me-md-2">Назад</a>
|
||||||
|
<button type="submit" class="btn btn-warning">Добавить в базу</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -15,6 +15,7 @@ urlpatterns = [
|
|||||||
path('cluster/', views.ClusterTestView.as_view(), name='cluster'),
|
path('cluster/', views.ClusterTestView.as_view(), name='cluster'),
|
||||||
path('vch-upload/', views.UploadVchLoadView.as_view(), name='vch_load'),
|
path('vch-upload/', views.UploadVchLoadView.as_view(), name='vch_load'),
|
||||||
path('vch-link/', views.LinkVchSigmaView.as_view(), name='link_vch_sigma'),
|
path('vch-link/', views.LinkVchSigmaView.as_view(), name='link_vch_sigma'),
|
||||||
|
path('kubsat-excel/', views.ProcessKubsatView.as_view(), name='kubsat_excel'),
|
||||||
# path('upload/', views.upload_file, name='upload_file'),
|
# path('upload/', views.upload_file, name='upload_file'),
|
||||||
|
|
||||||
]
|
]
|
||||||
@@ -10,12 +10,18 @@ from .models import (
|
|||||||
ObjItem,
|
ObjItem,
|
||||||
CustomUser
|
CustomUser
|
||||||
)
|
)
|
||||||
|
from mapsapp.models import Transponders
|
||||||
from datetime import datetime, time
|
from datetime import datetime, time
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from django.contrib.gis.geos import Point
|
from django.contrib.gis.geos import Point
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import io
|
||||||
|
from django.db.models import F, Count, Exists, OuterRef, Min, Max
|
||||||
|
from geopy.geocoders import Nominatim
|
||||||
|
import reverse_geocoder as rg
|
||||||
|
import time
|
||||||
|
|
||||||
def get_all_constants():
|
def get_all_constants():
|
||||||
sats = [sat.name for sat in Satellite.objects.all()]
|
sats = [sat.name for sat in Satellite.objects.all()]
|
||||||
@@ -74,7 +80,10 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite):
|
|||||||
freq = remove_str(stroka[1]['Частота, МГц'])
|
freq = remove_str(stroka[1]['Частота, МГц'])
|
||||||
freq_line = remove_str(stroka[1]['Полоса, МГц'])
|
freq_line = remove_str(stroka[1]['Полоса, МГц'])
|
||||||
v = remove_str(stroka[1]['Символьная скорость, БОД'])
|
v = remove_str(stroka[1]['Символьная скорость, БОД'])
|
||||||
mod_obj, _ = Modulation.objects.get_or_create(name=stroka[1]['Модуляция'].strip())
|
try:
|
||||||
|
mod_obj, _ = Modulation.objects.get_or_create(name=stroka[1]['Модуляция'].strip())
|
||||||
|
except AttributeError:
|
||||||
|
mod_obj, _ = Modulation.objects.get_or_create(name='-')
|
||||||
snr = remove_str(stroka[1]['ОСШ'])
|
snr = remove_str(stroka[1]['ОСШ'])
|
||||||
date = stroka[1]['Дата'].date()
|
date = stroka[1]['Дата'].date()
|
||||||
time_ = stroka[1]['Время']
|
time_ = stroka[1]['Время']
|
||||||
@@ -192,17 +201,7 @@ def get_point_from_json(filepath: str):
|
|||||||
|
|
||||||
|
|
||||||
def get_points_from_csv(file_content):
|
def get_points_from_csv(file_content):
|
||||||
import io
|
df = pd.read_csv(io.StringIO(file_content), sep=";",
|
||||||
if hasattr(file_content, 'read'):
|
|
||||||
content = file_content.read()
|
|
||||||
if isinstance(content, bytes):
|
|
||||||
content = content.decode('utf-8')
|
|
||||||
else:
|
|
||||||
if isinstance(file_content, bytes):
|
|
||||||
content = content.decode('utf-8')
|
|
||||||
else:
|
|
||||||
content = file_content
|
|
||||||
df = pd.read_csv(io.StringIO(content), sep=";",
|
|
||||||
names=['id', 'obj', 'lat', 'lon', 'h', 'time', 'sat', 'norad_id', 'freq', 'f_range', 'et', 'qaul', 'mir_1', 'mir_2', 'mir_3'])
|
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)
|
df[['lat', 'lon', 'freq', 'f_range']] = df[['lat', 'lon', 'freq', 'f_range']].replace(',', '.', regex=True).astype(float)
|
||||||
df['time'] = pd.to_datetime(df['time'], format='%d.%m.%Y %H:%M:%S')
|
df['time'] = pd.to_datetime(df['time'], format='%d.%m.%Y %H:%M:%S')
|
||||||
@@ -266,73 +265,22 @@ def get_points_from_csv(file_content):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
obj_item_obj.save()
|
obj_item_obj.save()
|
||||||
# df = pd.read_csv(filepath, 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)
|
|
||||||
# df['time'] = pd.to_datetime(df['time'], format='%d.%m.%Y %H:%M:%S')
|
|
||||||
# for row in df.iterrows():
|
|
||||||
# row = row[1]
|
|
||||||
# match row['obj'].split(' ')[-1]:
|
|
||||||
# case 'V':
|
|
||||||
# pol = 'Вертикальная'
|
|
||||||
# case 'H':
|
|
||||||
# pol = 'Горизонтальная'
|
|
||||||
# case 'R':
|
|
||||||
# pol = 'Правая'
|
|
||||||
# case 'L':
|
|
||||||
# pol = 'Левая'
|
|
||||||
# case _:
|
|
||||||
# pol = '-'
|
|
||||||
# pol_obj, _ = Polarization.objects.get_or_create(
|
|
||||||
# name=pol
|
|
||||||
# )
|
|
||||||
# sat_obj, _ = Satellite.objects.get_or_create(
|
|
||||||
# name=row['sat'],
|
|
||||||
# defaults={'norad': row['norad_id']}
|
|
||||||
# )
|
|
||||||
# mir_1_obj, _ = Mirror.objects.get_or_create(
|
|
||||||
# name=row['mir_1']
|
|
||||||
# )
|
|
||||||
# mir_2_obj, _ = Mirror.objects.get_or_create(
|
|
||||||
# name=row['mir_2']
|
|
||||||
# )
|
|
||||||
# mir_lst = [row['mir_1'], row['mir_2']]
|
|
||||||
# if not pd.isna(row['mir_3']):
|
|
||||||
# mir_3_obj, _ = Mirror.objects.get_or_create(
|
|
||||||
|
|
||||||
# name=row['mir_3']
|
|
||||||
# )
|
|
||||||
# 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)}
|
|
||||||
# )
|
|
||||||
|
|
||||||
# geo_obj, _ = Geo.objects.get_or_create(
|
|
||||||
# timestamp=row['time'],
|
|
||||||
# coords=Point(row['lon'], row['lat'], srid=4326),
|
|
||||||
# defaults={
|
|
||||||
# 'is_average': False,
|
|
||||||
# 'id_user_add': CustomUser.objects.get(id=1),
|
|
||||||
# }
|
|
||||||
# )
|
|
||||||
# geo_obj.mirrors.set(Mirror.objects.filter(name__in=mir_lst))
|
|
||||||
|
|
||||||
# obj_item_obj, _ = ObjItem.objects.get_or_create(
|
|
||||||
# name=row['obj'],
|
|
||||||
# # id_satellite=sat_obj,
|
|
||||||
# id_vch_load=vch_load_obj,
|
|
||||||
# id_geo=geo_obj,
|
|
||||||
# defaults={
|
|
||||||
# 'id_user_add': CustomUser.objects.get(id=1)
|
|
||||||
# }
|
|
||||||
# )
|
|
||||||
# obj_item_obj.save()
|
|
||||||
|
|
||||||
|
|
||||||
def get_vch_load_from_html(file, sat: Satellite) -> None:
|
def get_vch_load_from_html(file, sat: Satellite) -> None:
|
||||||
|
filename = file.name.split('_')
|
||||||
|
transfer = filename[3]
|
||||||
|
match filename[2]:
|
||||||
|
case 'H':
|
||||||
|
pol = 'Горизонтальная'
|
||||||
|
case 'V':
|
||||||
|
pol = 'Вертикальная'
|
||||||
|
case 'R':
|
||||||
|
pol = 'Правая'
|
||||||
|
case 'L':
|
||||||
|
pol = 'Левая'
|
||||||
|
case _:
|
||||||
|
pol = '-'
|
||||||
|
|
||||||
tables = pd.read_html(file, encoding='windows-1251')
|
tables = pd.read_html(file, encoding='windows-1251')
|
||||||
df = tables[0]
|
df = tables[0]
|
||||||
df = df.drop(0).reset_index(drop=True)
|
df = df.drop(0).reset_index(drop=True)
|
||||||
@@ -362,6 +310,10 @@ def get_vch_load_from_html(file, sat: Satellite) -> None:
|
|||||||
else:
|
else:
|
||||||
pack = None
|
pack = None
|
||||||
|
|
||||||
|
polarization, _ = Polarization.objects.get_or_create(
|
||||||
|
name=pol
|
||||||
|
)
|
||||||
|
|
||||||
mod, _ = Modulation.objects.get_or_create(
|
mod, _ = Modulation.objects.get_or_create(
|
||||||
name=value['Модуляция']
|
name=value['Модуляция']
|
||||||
)
|
)
|
||||||
@@ -372,7 +324,10 @@ def get_vch_load_from_html(file, sat: Satellite) -> None:
|
|||||||
id_satellite=sat,
|
id_satellite=sat,
|
||||||
frequency=value['Частота, МГц'],
|
frequency=value['Частота, МГц'],
|
||||||
freq_range=value['Полоса, МГц'],
|
freq_range=value['Полоса, МГц'],
|
||||||
|
polarization=polarization,
|
||||||
defaults={
|
defaults={
|
||||||
|
"transfer": float(transfer),
|
||||||
|
# "polarization": polarization,
|
||||||
"status": value['Статус'],
|
"status": value['Статус'],
|
||||||
"power": value['Мощность, дБм'],
|
"power": value['Мощность, дБм'],
|
||||||
"bod_velocity": bod_velocity,
|
"bod_velocity": bod_velocity,
|
||||||
@@ -386,15 +341,6 @@ def get_vch_load_from_html(file, sat: Satellite) -> None:
|
|||||||
)
|
)
|
||||||
sigma_load.save()
|
sigma_load.save()
|
||||||
|
|
||||||
def define_ku_transfer(min_freq: float, max_freq: float) -> int | None:
|
|
||||||
fss = (10700, 11700)
|
|
||||||
dss = (11700, 12750)
|
|
||||||
if min_freq + 9750 >= fss[0] and max_freq + 9750 <= fss[1]:
|
|
||||||
return 9750
|
|
||||||
elif min_freq + 10750 >= dss[0] and max_freq + 10750 <= dss[1]:
|
|
||||||
return 10750
|
|
||||||
return None
|
|
||||||
|
|
||||||
def compare_and_link_vch_load(sat_id: Satellite, eps_freq: float, eps_frange: float, ku_range: float):
|
def compare_and_link_vch_load(sat_id: Satellite, eps_freq: float, eps_frange: float, ku_range: float):
|
||||||
item_obj = ObjItem.objects.filter(id_vch_load__id_satellite=sat_id)
|
item_obj = ObjItem.objects.filter(id_vch_load__id_satellite=sat_id)
|
||||||
vch_sigma = SigmaParameter.objects.filter(id_satellite=sat_id)
|
vch_sigma = SigmaParameter.objects.filter(id_satellite=sat_id)
|
||||||
@@ -406,10 +352,62 @@ def compare_and_link_vch_load(sat_id: Satellite, eps_freq: float, eps_frange: fl
|
|||||||
continue
|
continue
|
||||||
# if unique_points = Point.objects.order_by('frequency').distinct('frequency')
|
# if unique_points = Point.objects.order_by('frequency').distinct('frequency')
|
||||||
for sigma in vch_sigma:
|
for sigma in vch_sigma:
|
||||||
if abs(sigma.frequency + ku_range - vch_load.frequency) <= vch_load.frequency*eps_freq/100 and abs(sigma.freq_range - vch_load.freq_range) <= vch_load.freq_range*eps_frange/100:
|
if (
|
||||||
|
abs(sigma.transfer_frequency - vch_load.frequency) <= vch_load.frequency*eps_freq/100 and
|
||||||
|
abs(sigma.freq_range - vch_load.freq_range) <= vch_load.freq_range*eps_frange/100 and
|
||||||
|
sigma.polarization == vch_load.polarization
|
||||||
|
):
|
||||||
sigma.parameter = vch_load
|
sigma.parameter = vch_load
|
||||||
sigma.save()
|
sigma.save()
|
||||||
link_count += 1
|
link_count += 1
|
||||||
return obj_count, link_count
|
return obj_count, link_count
|
||||||
|
|
||||||
|
def kub_report(data_in: io.StringIO) -> pd.DataFrame:
|
||||||
|
df_in = pd.read_excel(data_in)
|
||||||
|
df = pd.DataFrame(columns=['Дата', 'Широта', 'Долгота',
|
||||||
|
'Высота', 'Населённый пункт', 'ИСЗ',
|
||||||
|
'Прямой канал, МГц', 'Обратный канал, МГц', 'Перенос, МГц', 'Полоса, МГц', 'Зеркала'])
|
||||||
|
for row in df_in.iterrows():
|
||||||
|
value = row[1]
|
||||||
|
date = datetime.date(datetime.now())
|
||||||
|
lat = value['Широта, град']
|
||||||
|
lon = value['Долгота, град']
|
||||||
|
isz = value['ИСЗ']
|
||||||
|
downlink = value['Обратный канал, МГц']
|
||||||
|
freq_range = value['Полоса, МГц']
|
||||||
|
norad = int(re.findall(r'\((\d+)\)', isz)[0])
|
||||||
|
sat_obj = Satellite.objects.get(norad=norad)
|
||||||
|
pol_obj = Polarization.objects.get(name=value['Поляризация'].strip())
|
||||||
|
transponder = Transponders.objects.filter(
|
||||||
|
sat_id=sat_obj,
|
||||||
|
polarization=pol_obj,
|
||||||
|
downlink__gte=downlink - F('frequency_range')/2,
|
||||||
|
downlink__lte=downlink + F('frequency_range')/2,
|
||||||
|
).first()
|
||||||
|
# try:
|
||||||
|
# location = geolocator.reverse(f"{lat}, {lon}", language="ru").raw['address']
|
||||||
|
# loc_name = location.get('city', '') or location.get('town', '') or location.get('province', '') or location.get('country', '')
|
||||||
|
# except AttributeError:
|
||||||
|
# loc_name = ''
|
||||||
|
# time.sleep(1)
|
||||||
|
loc_name = ''
|
||||||
|
if transponder: #and not (len(transponder) > 1):
|
||||||
|
transfer = transponder.transfer
|
||||||
|
uplink = transfer + downlink
|
||||||
|
new_row = pd.DataFrame([{'Дата': date,
|
||||||
|
'Широта': lat,
|
||||||
|
'Долгота': lon,
|
||||||
|
'Высота': 0.0,
|
||||||
|
'Населённый пункт': loc_name,
|
||||||
|
'ИСЗ': isz,
|
||||||
|
'Прямой канал, МГц': uplink,
|
||||||
|
'Обратный канал, МГц': downlink,
|
||||||
|
'Перенос, МГц': transfer,
|
||||||
|
'Полоса, МГц': freq_range,
|
||||||
|
'Зеркала': ''
|
||||||
|
}])
|
||||||
|
df = pd.concat([df, new_row], ignore_index=True)
|
||||||
|
else:
|
||||||
|
print("Ничего не найдено в транспондерах")
|
||||||
|
return df
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse, HttpResponse
|
||||||
from django.views.decorators.http import require_GET
|
from django.views.decorators.http import require_GET
|
||||||
from django.contrib.admin.views.decorators import staff_member_required
|
from django.contrib.admin.views.decorators import staff_member_required
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
@@ -13,13 +13,15 @@ from .utils import (
|
|||||||
add_satellite_list,
|
add_satellite_list,
|
||||||
get_points_from_csv,
|
get_points_from_csv,
|
||||||
get_vch_load_from_html,
|
get_vch_load_from_html,
|
||||||
compare_and_link_vch_load
|
compare_and_link_vch_load,
|
||||||
|
kub_report
|
||||||
)
|
)
|
||||||
from mapsapp.utils import parse_transponders_from_json
|
from mapsapp.utils import parse_transponders_from_json, parse_transponders_from_xml
|
||||||
from .forms import LoadExcelData, LoadCsvData, UploadFileForm, VchLinkForm
|
from .forms import LoadExcelData, LoadCsvData, UploadFileForm, VchLinkForm, UploadVchLoad, NewEventForm
|
||||||
from .models import ObjItem
|
from .models import ObjItem
|
||||||
from .clusters import get_clusters
|
from .clusters import get_clusters
|
||||||
from dbapp.settings import BASE_DIR
|
from io import BytesIO
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AddSatellitesView(View):
|
class AddSatellitesView(View):
|
||||||
@@ -27,13 +29,33 @@ class AddSatellitesView(View):
|
|||||||
add_satellite_list()
|
add_satellite_list()
|
||||||
return redirect('home')
|
return redirect('home')
|
||||||
|
|
||||||
class AddTranspondersView(View):
|
# class AddTranspondersView(View):
|
||||||
def get(self, request):
|
# def get(self, request):
|
||||||
|
# try:
|
||||||
|
# parse_transponders_from_json(BASE_DIR / "transponders.json")
|
||||||
|
# except FileNotFoundError:
|
||||||
|
# print("Файл не найден")
|
||||||
|
# return redirect('home')
|
||||||
|
|
||||||
|
class AddTranspondersView(FormView):
|
||||||
|
template_name = 'mainapp/transponders_upload.html'
|
||||||
|
form_class = UploadFileForm
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
uploaded_file = self.request.FILES['file']
|
||||||
try:
|
try:
|
||||||
parse_transponders_from_json(BASE_DIR / "transponders.json")
|
content = uploaded_file.read()
|
||||||
except FileNotFoundError:
|
parse_transponders_from_xml(BytesIO(content))
|
||||||
print("Файл не найден")
|
messages.success(self.request, "Файл успешно обработан")
|
||||||
return redirect('home')
|
except ValueError as e:
|
||||||
|
messages.error(self.request, f"Ошибка при чтении таблиц: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(self.request, f"Неизвестная ошибка: {e}")
|
||||||
|
return redirect('add_trans')
|
||||||
|
|
||||||
|
def form_invalid(self, form):
|
||||||
|
messages.error(self.request, "Форма заполнена некорректно.")
|
||||||
|
return super().form_invalid(form)
|
||||||
|
|
||||||
class HomePageView(TemplateView):
|
class HomePageView(TemplateView):
|
||||||
template_name = 'mainapp/home.html'
|
template_name = 'mainapp/home.html'
|
||||||
@@ -118,22 +140,7 @@ class LoadCsvDataView(FormView):
|
|||||||
messages.error(self.request, "Форма заполнена некорректно.")
|
messages.error(self.request, "Форма заполнена некорректно.")
|
||||||
return super().form_invalid(form)
|
return super().form_invalid(form)
|
||||||
|
|
||||||
# def upload_file(request):
|
|
||||||
# if request.method == 'POST' and request.FILES:
|
|
||||||
# form = UploadFileForm(request.POST, request.FILES)
|
|
||||||
# if form.is_valid():
|
|
||||||
# uploaded_file = request.FILES['file']
|
|
||||||
# # Обработка текстового файла, например:
|
|
||||||
# df = pd.read_csv(uploaded_file)
|
|
||||||
# df = pd.read_csv(filepath, 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)
|
|
||||||
# df['time'] = pd.to_datetime(df['time'], format='%d.%m.%Y %H:%M:%S')
|
|
||||||
# get_points_from_csv(df)
|
|
||||||
# return JsonResponse({'status': 'success'})
|
|
||||||
# else:
|
|
||||||
# return JsonResponse({'status': 'error', 'errors': form.errors}, status=400)
|
|
||||||
# return render(request, 'mainapp/add_data_from_csv.html')
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
@method_decorator(staff_member_required, name='dispatch')
|
@method_decorator(staff_member_required, name='dispatch')
|
||||||
@@ -162,7 +169,6 @@ class ShowMapView(UserPassesTestMixin, View):
|
|||||||
'frequency': p["freq"]
|
'frequency': p["freq"]
|
||||||
})
|
})
|
||||||
|
|
||||||
# Преобразуем в список словарей для удобства в шаблоне
|
|
||||||
groups = [
|
groups = [
|
||||||
{
|
{
|
||||||
"name": name,
|
"name": name,
|
||||||
@@ -190,7 +196,7 @@ class ClusterTestView(View):
|
|||||||
|
|
||||||
class UploadVchLoadView(FormView):
|
class UploadVchLoadView(FormView):
|
||||||
template_name = 'mainapp/upload_html.html'
|
template_name = 'mainapp/upload_html.html'
|
||||||
form_class = UploadFileForm
|
form_class = UploadVchLoad
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
selected_sat = form.cleaned_data['sat_choice']
|
selected_sat = form.cleaned_data['sat_choice']
|
||||||
@@ -224,4 +230,38 @@ class LinkVchSigmaView(FormView):
|
|||||||
return redirect('link_vch_sigma')
|
return redirect('link_vch_sigma')
|
||||||
|
|
||||||
def form_invalid(self, form):
|
def form_invalid(self, form):
|
||||||
return self.render_to_response(self.get_context_data(form=form))
|
return self.render_to_response(self.get_context_data(form=form))
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessKubsatView(FormView):
|
||||||
|
template_name = 'mainapp/process_kubsat.html'
|
||||||
|
form_class = NewEventForm
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
# selected_sat = form.cleaned_data['sat_choice']
|
||||||
|
# selected_pol = form.cleaned_data['pol_choice']
|
||||||
|
uploaded_file = self.request.FILES['file']
|
||||||
|
try:
|
||||||
|
content = uploaded_file.read()
|
||||||
|
df = kub_report(BytesIO(content))
|
||||||
|
output = BytesIO()
|
||||||
|
with pd.ExcelWriter(output, engine='openpyxl') as writer:
|
||||||
|
df.to_excel(writer, index=False, sheet_name='Результат')
|
||||||
|
output.seek(0)
|
||||||
|
|
||||||
|
response = HttpResponse(
|
||||||
|
output.getvalue(),
|
||||||
|
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
)
|
||||||
|
response['Content-Disposition'] = f'attachment; filename="kubsat_report.xlsx"'
|
||||||
|
|
||||||
|
messages.success(self.request, "Событие успешно обработано!")
|
||||||
|
return response
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(self.request, f"Ошибка при обработке файла: {str(e)}")
|
||||||
|
return redirect('kubsat_excel')
|
||||||
|
# return redirect('kubsat_excel')
|
||||||
|
|
||||||
|
def form_invalid(self, form):
|
||||||
|
messages.error(self.request, "Форма заполнена некорректно.")
|
||||||
|
return super().form_invalid(form)
|
||||||
@@ -5,20 +5,24 @@ from more_admin_filters import MultiSelectDropdownFilter, MultiSelectFilter, Mul
|
|||||||
from import_export.admin import ImportExportActionModelAdmin
|
from import_export.admin import ImportExportActionModelAdmin
|
||||||
|
|
||||||
@admin.register(Transponders)
|
@admin.register(Transponders)
|
||||||
class PolarizationAdmin(ImportExportActionModelAdmin, admin.ModelAdmin):
|
class TranspondersAdmin(ImportExportActionModelAdmin, admin.ModelAdmin):
|
||||||
list_display = (
|
list_display = (
|
||||||
"sat_id",
|
"sat_id",
|
||||||
"name",
|
"name",
|
||||||
"zone_name",
|
"zone_name",
|
||||||
"frequency",
|
"downlink",
|
||||||
|
"uplink",
|
||||||
"frequency_range",
|
"frequency_range",
|
||||||
|
"transfer",
|
||||||
"polarization",
|
"polarization",
|
||||||
)
|
)
|
||||||
list_filter = (
|
list_filter = (
|
||||||
("polarization", MultiSelectRelatedDropdownFilter),
|
("polarization", MultiSelectRelatedDropdownFilter),
|
||||||
("sat_id", MultiSelectRelatedDropdownFilter),
|
("sat_id", MultiSelectRelatedDropdownFilter),
|
||||||
("frequency", NumericRangeFilterBuilder()),
|
# ("frequency", NumericRangeFilterBuilder()),
|
||||||
"zone_name"
|
"zone_name"
|
||||||
)
|
)
|
||||||
search_fields = ("name",)
|
search_fields = ("name", "sat_id__name")
|
||||||
ordering = ("name",)
|
ordering = ("name",)
|
||||||
|
# def sat_name(self, obj):
|
||||||
|
# return
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-10-27 12:20
|
||||||
|
|
||||||
|
import django.db.models.expressions
|
||||||
|
import django.db.models.functions.math
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mapsapp', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='transponders',
|
||||||
|
name='frequency',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='transponders',
|
||||||
|
name='downlink',
|
||||||
|
field=models.FloatField(blank=True, null=True, verbose_name='Downlink'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='transponders',
|
||||||
|
name='uplink',
|
||||||
|
field=models.FloatField(blank=True, null=True, verbose_name='Uplink'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='transponders',
|
||||||
|
name='transfer',
|
||||||
|
field=models.GeneratedField(db_persist=True, expression=models.ExpressionWrapper(django.db.models.functions.math.Abs(django.db.models.expressions.CombinedExpression(models.F('downlink'), '-', models.F('uplink'))), output_field=models.FloatField()), null=True, output_field=models.FloatField(), verbose_name='Расстояние между купсатом и гео, км'),
|
||||||
|
),
|
||||||
|
]
|
||||||
20
dbapp/mapsapp/migrations/0003_alter_transponders_transfer.py
Normal file
20
dbapp/mapsapp/migrations/0003_alter_transponders_transfer.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-10-27 13:10
|
||||||
|
|
||||||
|
import django.db.models.expressions
|
||||||
|
import django.db.models.functions.math
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mapsapp', '0002_remove_transponders_frequency_transponders_downlink_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='transponders',
|
||||||
|
name='transfer',
|
||||||
|
field=models.GeneratedField(db_persist=True, expression=models.ExpressionWrapper(django.db.models.functions.math.Abs(django.db.models.expressions.CombinedExpression(models.F('downlink'), '-', models.F('uplink'))), output_field=models.FloatField()), null=True, output_field=models.FloatField(), verbose_name='Перенос'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-10-28 05:44
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mapsapp', '0003_alter_transponders_transfer'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='transponders',
|
||||||
|
name='zone_name',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Название зоны'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-10-29 14:00
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mapsapp', '0004_alter_transponders_zone_name'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='transponders',
|
||||||
|
name='frequency_range',
|
||||||
|
field=models.FloatField(blank=True, null=True, verbose_name='Полоса'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,15 +1,27 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from mainapp.models import Satellite, Polarization, get_default_polarization
|
from mainapp.models import Satellite, Polarization, get_default_polarization
|
||||||
|
from django.db.models import F, ExpressionWrapper
|
||||||
|
from django.db.models.functions import Abs
|
||||||
|
|
||||||
class Transponders(models.Model):
|
class Transponders(models.Model):
|
||||||
name = models.CharField(max_length=30, null=True, blank=True, verbose_name="Название транспондера")
|
name = models.CharField(max_length=30, null=True, blank=True, verbose_name="Название транспондера")
|
||||||
frequency = models.FloatField(blank=True, null=True, verbose_name="Центральная частота")
|
downlink = models.FloatField(blank=True, null=True, verbose_name="Downlink")
|
||||||
frequency_range = models.FloatField(blank=True, null=True, verbose_name="Полоса частот")
|
frequency_range = models.FloatField(blank=True, null=True, verbose_name="Полоса")
|
||||||
zone_name = models.CharField(max_length=60, blank=True, null=True, verbose_name="Название зоны")
|
uplink = models.FloatField(blank=True, null=True, verbose_name="Uplink")
|
||||||
|
zone_name = models.CharField(max_length=255, blank=True, null=True, verbose_name="Название зоны")
|
||||||
polarization = models.ForeignKey(
|
polarization = models.ForeignKey(
|
||||||
Polarization, default=get_default_polarization, on_delete=models.SET_DEFAULT, related_name="tran_polarizations", null=True, blank=True, verbose_name="Поляризация"
|
Polarization, default=get_default_polarization, on_delete=models.SET_DEFAULT, related_name="tran_polarizations", null=True, blank=True, verbose_name="Поляризация"
|
||||||
)
|
)
|
||||||
sat_id = models.ForeignKey(Satellite, on_delete=models.PROTECT, related_name="tran_satellite", verbose_name="Спутник")
|
sat_id = models.ForeignKey(Satellite, on_delete=models.PROTECT, related_name="tran_satellite", verbose_name="Спутник")
|
||||||
|
transfer =models.GeneratedField(
|
||||||
|
expression=ExpressionWrapper(
|
||||||
|
Abs(F('downlink') - F('uplink')),
|
||||||
|
output_field=models.FloatField()
|
||||||
|
),
|
||||||
|
output_field=models.FloatField(),
|
||||||
|
db_persist=True,
|
||||||
|
null=True, blank=True, verbose_name="Перенос"
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import re
|
|||||||
import json
|
import json
|
||||||
from .models import Transponders
|
from .models import Transponders
|
||||||
from mainapp.models import Polarization, Satellite
|
from mainapp.models import Polarization, Satellite
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
def search_satellite_on_page(data: dict, satellite_name: str):
|
def search_satellite_on_page(data: dict, satellite_name: str):
|
||||||
for pos, value in data.get('page', {}).get('positions').items():
|
for pos, value in data.get('page', {}).get('positions').items():
|
||||||
@@ -90,3 +91,68 @@ def parse_transponders_from_json(filepath: str):
|
|||||||
)
|
)
|
||||||
tran_obj.save()
|
tran_obj.save()
|
||||||
|
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
|
def parse_transponders_from_xml(data_in: BytesIO):
|
||||||
|
|
||||||
|
tree = etree.parse(data_in)
|
||||||
|
ns = {
|
||||||
|
'i': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||||
|
'ns': 'http://schemas.datacontract.org/2004/07/Geolocation.Domain.Utils.Repository.SatellitesSerialization.Memos',
|
||||||
|
'tr': 'http://schemas.datacontract.org/2004/07/Geolocation.Common.Extensions'
|
||||||
|
}
|
||||||
|
satellites = tree.xpath('//ns:SatelliteMemo', namespaces=ns)
|
||||||
|
for sat in satellites[:]:
|
||||||
|
name = sat.xpath('./ns:name/text()', namespaces=ns)[0]
|
||||||
|
if name == 'X' or 'DONT USE' in name:
|
||||||
|
continue
|
||||||
|
norad = sat.xpath('./ns:norad/text()', namespaces=ns)
|
||||||
|
beams = sat.xpath('.//ns:BeamMemo', namespaces=ns)
|
||||||
|
zones = {}
|
||||||
|
for zone in beams:
|
||||||
|
zone_name = zone.xpath('./ns:name/text()', namespaces=ns)[0] if zone.xpath('./ns:name/text()', namespaces=ns) else '-'
|
||||||
|
zones[zone.xpath('./ns:id/text()', namespaces=ns)[0]] = {
|
||||||
|
"name": zone_name,
|
||||||
|
"pol": zone.xpath('./ns:polarization/text()', namespaces=ns)[0],
|
||||||
|
}
|
||||||
|
transponders = sat.xpath('.//ns:TransponderMemo', namespaces=ns)
|
||||||
|
for transponder in transponders:
|
||||||
|
tr_id = transponder.xpath('./ns:downlinkBeamId/text()', namespaces=ns)[0]
|
||||||
|
downlink_start = float(transponder.xpath('./ns:downlinkFrequency/tr:start/text()', namespaces=ns)[0])
|
||||||
|
downlink_end = float(transponder.xpath('./ns:downlinkFrequency/tr:end/text()', namespaces=ns)[0])
|
||||||
|
uplink_start = float(transponder.xpath('./ns:uplinkFrequency/tr:start/text()', namespaces=ns)[0])
|
||||||
|
uplink_end = float(transponder.xpath('./ns:uplinkFrequency/tr:end/text()', namespaces=ns)[0])
|
||||||
|
tr_data = zones[tr_id]
|
||||||
|
# p = tr_data['pol'][0] if tr_data['pol'] else '-'
|
||||||
|
match tr_data['pol']:
|
||||||
|
case 'Horizontal':
|
||||||
|
pol = 'Горизонтальная'
|
||||||
|
case 'Vertical':
|
||||||
|
pol = 'Вертикальная'
|
||||||
|
case 'CircularRight':
|
||||||
|
pol = 'Правая'
|
||||||
|
case 'CircularLeft':
|
||||||
|
pol = 'Левая'
|
||||||
|
case _:
|
||||||
|
pol = '-'
|
||||||
|
tr_name = transponder.xpath('./ns:name/text()', namespaces=ns)[0]
|
||||||
|
|
||||||
|
pol_obj, _ = Polarization.objects.get_or_create(name=pol)
|
||||||
|
sat_obj, _ = Satellite.objects.get_or_create(
|
||||||
|
name=name,
|
||||||
|
defaults={
|
||||||
|
"norad": int(norad[0]) if norad else -1
|
||||||
|
})
|
||||||
|
trans_obj, _ = Transponders.objects.get_or_create(
|
||||||
|
polarization=pol_obj,
|
||||||
|
downlink=(downlink_start+downlink_end)/2/1000000,
|
||||||
|
uplink=(uplink_start+uplink_end)/2/1000000,
|
||||||
|
frequency_range=abs(downlink_end-downlink_start)/1000000,
|
||||||
|
name=tr_name,
|
||||||
|
defaults={
|
||||||
|
"zone_name": tr_data['name'],
|
||||||
|
"sat_id": sat_obj,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
trans_obj.save()
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ dependencies = [
|
|||||||
"django-leaflet>=0.32.0",
|
"django-leaflet>=0.32.0",
|
||||||
"django-map-widgets>=0.5.1",
|
"django-map-widgets>=0.5.1",
|
||||||
"django-more-admin-filters>=1.13",
|
"django-more-admin-filters>=1.13",
|
||||||
"gdal",
|
"dotenv>=0.9.9",
|
||||||
|
"geopy>=2.4.1",
|
||||||
|
"gunicorn>=23.0.0",
|
||||||
"lxml>=6.0.2",
|
"lxml>=6.0.2",
|
||||||
"matplotlib>=3.10.7",
|
"matplotlib>=3.10.7",
|
||||||
"numpy>=2.3.3",
|
"numpy>=2.3.3",
|
||||||
@@ -28,9 +30,11 @@ dependencies = [
|
|||||||
"psycopg>=3.2.10",
|
"psycopg>=3.2.10",
|
||||||
"redis>=6.4.0",
|
"redis>=6.4.0",
|
||||||
"requests>=2.32.5",
|
"requests>=2.32.5",
|
||||||
|
"reverse-geocoder>=1.5.1",
|
||||||
"scikit-learn>=1.7.2",
|
"scikit-learn>=1.7.2",
|
||||||
"setuptools>=80.9.0",
|
"setuptools>=80.9.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.uv.sources]
|
|
||||||
gdal = { path = "gdal-3.10.2-cp313-cp313-win_amd64.whl" }
|
[dependency-groups]
|
||||||
|
dev = []
|
||||||
|
|||||||
78
dbapp/uv.lock
generated
78
dbapp/uv.lock
generated
@@ -212,7 +212,9 @@ dependencies = [
|
|||||||
{ name = "django-leaflet" },
|
{ name = "django-leaflet" },
|
||||||
{ name = "django-map-widgets" },
|
{ name = "django-map-widgets" },
|
||||||
{ name = "django-more-admin-filters" },
|
{ name = "django-more-admin-filters" },
|
||||||
{ name = "gdal" },
|
{ name = "dotenv" },
|
||||||
|
{ name = "geopy" },
|
||||||
|
{ name = "gunicorn" },
|
||||||
{ name = "lxml" },
|
{ name = "lxml" },
|
||||||
{ name = "matplotlib" },
|
{ name = "matplotlib" },
|
||||||
{ name = "numpy" },
|
{ name = "numpy" },
|
||||||
@@ -221,6 +223,7 @@ dependencies = [
|
|||||||
{ name = "psycopg" },
|
{ name = "psycopg" },
|
||||||
{ name = "redis" },
|
{ name = "redis" },
|
||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
|
{ name = "reverse-geocoder" },
|
||||||
{ name = "scikit-learn" },
|
{ name = "scikit-learn" },
|
||||||
{ name = "setuptools" },
|
{ name = "setuptools" },
|
||||||
]
|
]
|
||||||
@@ -241,7 +244,9 @@ requires-dist = [
|
|||||||
{ name = "django-leaflet", specifier = ">=0.32.0" },
|
{ name = "django-leaflet", specifier = ">=0.32.0" },
|
||||||
{ name = "django-map-widgets", specifier = ">=0.5.1" },
|
{ name = "django-map-widgets", specifier = ">=0.5.1" },
|
||||||
{ name = "django-more-admin-filters", specifier = ">=1.13" },
|
{ name = "django-more-admin-filters", specifier = ">=1.13" },
|
||||||
{ name = "gdal", path = "gdal-3.10.2-cp313-cp313-win_amd64.whl" },
|
{ name = "dotenv", specifier = ">=0.9.9" },
|
||||||
|
{ name = "geopy", specifier = ">=2.4.1" },
|
||||||
|
{ name = "gunicorn", specifier = ">=23.0.0" },
|
||||||
{ name = "lxml", specifier = ">=6.0.2" },
|
{ name = "lxml", specifier = ">=6.0.2" },
|
||||||
{ name = "matplotlib", specifier = ">=3.10.7" },
|
{ name = "matplotlib", specifier = ">=3.10.7" },
|
||||||
{ name = "numpy", specifier = ">=2.3.3" },
|
{ name = "numpy", specifier = ">=2.3.3" },
|
||||||
@@ -250,10 +255,14 @@ requires-dist = [
|
|||||||
{ name = "psycopg", specifier = ">=3.2.10" },
|
{ name = "psycopg", specifier = ">=3.2.10" },
|
||||||
{ name = "redis", specifier = ">=6.4.0" },
|
{ name = "redis", specifier = ">=6.4.0" },
|
||||||
{ name = "requests", specifier = ">=2.32.5" },
|
{ name = "requests", specifier = ">=2.32.5" },
|
||||||
|
{ name = "reverse-geocoder", specifier = ">=1.5.1" },
|
||||||
{ name = "scikit-learn", specifier = ">=1.7.2" },
|
{ name = "scikit-learn", specifier = ">=1.7.2" },
|
||||||
{ name = "setuptools", specifier = ">=80.9.0" },
|
{ name = "setuptools", specifier = ">=80.9.0" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[package.metadata.requires-dev]
|
||||||
|
dev = []
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diff-match-patch"
|
name = "diff-match-patch"
|
||||||
version = "20241021"
|
version = "20241021"
|
||||||
@@ -410,6 +419,17 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/87/7c/4b261b96b357d94ef267f39856ef0bb72a33f078a38bd22ee96d168fe272/django_more_admin_filters-1.13-py3-none-any.whl", hash = "sha256:df4d46e4b589566b85f149ea5b7558c6cc4ae22b0d264973f8d4a2d478ef5120", size = 147360, upload-time = "2025-06-06T11:26:42.964Z" },
|
{ url = "https://files.pythonhosted.org/packages/87/7c/4b261b96b357d94ef267f39856ef0bb72a33f078a38bd22ee96d168fe272/django_more_admin_filters-1.13-py3-none-any.whl", hash = "sha256:df4d46e4b589566b85f149ea5b7558c6cc4ae22b0d264973f8d4a2d478ef5120", size = 147360, upload-time = "2025-06-06T11:26:42.964Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dotenv"
|
||||||
|
version = "0.9.9"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "python-dotenv" },
|
||||||
|
]
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892, upload-time = "2025-02-19T22:15:01.647Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "et-xmlfile"
|
name = "et-xmlfile"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@@ -453,16 +473,37 @@ wheels = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdal"
|
name = "geographiclib"
|
||||||
version = "3.10.2"
|
version = "2.1"
|
||||||
source = { path = "gdal-3.10.2-cp313-cp313-win_amd64.whl" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/df/78/4892343230a9d29faa1364564e525307a37e54ad776ea62c12129dbba704/geographiclib-2.1.tar.gz", hash = "sha256:6a6545e6262d0ed3522e13c515713718797e37ed8c672c31ad7b249f372ef108", size = 37004, upload-time = "2025-08-21T21:34:26Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ filename = "gdal-3.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:d6aae781b9847065f831f3457c6c01d0b9272818656031d723dc88c160a8ec26" },
|
{ url = "https://files.pythonhosted.org/packages/31/b3/802576f2ea5dcb48501bb162e4c7b7b3ca5654a42b2c968ef98a797a4c79/geographiclib-2.1-py3-none-any.whl", hash = "sha256:e2a873b9b9e7fc38721ad73d5f4e6c9ed140d428a339970f505c07056997d40b", size = 40740, upload-time = "2025-08-21T21:34:24.955Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[[package]]
|
||||||
requires-dist = [{ name = "numpy", marker = "extra == 'numpy'", specifier = ">1.0.0" }]
|
name = "geopy"
|
||||||
provides-extras = ["numpy"]
|
version = "2.4.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "geographiclib" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/0e/fd/ef6d53875ceab72c1fad22dbed5ec1ad04eb378c2251a6a8024bad890c3b/geopy-2.4.1.tar.gz", hash = "sha256:50283d8e7ad07d89be5cb027338c6365a32044df3ae2556ad3f52f4840b3d0d1", size = 117625, upload-time = "2023-11-23T21:49:32.734Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e5/15/cf2a69ade4b194aa524ac75112d5caac37414b20a3a03e6865dfe0bd1539/geopy-2.4.1-py3-none-any.whl", hash = "sha256:ae8b4bc5c1131820f4d75fce9d4aaaca0c85189b3aa5d64c3dcaf5e3b7b882a7", size = 125437, upload-time = "2023-11-23T21:49:30.421Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gunicorn"
|
||||||
|
version = "23.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "packaging" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload-time = "2024-08-10T20:25:24.996Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
@@ -851,6 +892,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-dotenv"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-slugify"
|
name = "python-slugify"
|
||||||
version = "8.0.4"
|
version = "8.0.4"
|
||||||
@@ -896,6 +946,16 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
|
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "reverse-geocoder"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "numpy" },
|
||||||
|
{ name = "scipy" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/0b/0f/b7d5d4b36553731f11983e19e1813a1059ad0732c5162c01b3220c927d31/reverse_geocoder-1.5.1.tar.gz", hash = "sha256:2a2e781b5f69376d922b78fe8978f1350c84fce0ddb07e02c834ecf98b57c75c", size = 2246559, upload-time = "2016-09-15T16:46:46.277Z" }
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scikit-learn"
|
name = "scikit-learn"
|
||||||
version = "1.7.2"
|
version = "1.7.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user