Compare commits
2 Commits
cf3c7ee01a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| f5875e5b87 | |||
| f79efd88e5 |
@@ -218,7 +218,7 @@ const requestsTable = new Tabulator("#requestsTable", {
|
|||||||
selectableRangeMode: "click",
|
selectableRangeMode: "click",
|
||||||
pagination: true,
|
pagination: true,
|
||||||
paginationSize: 50,
|
paginationSize: 50,
|
||||||
paginationSizeSelector: [25, 50, 100, 200],
|
paginationSizeSelector: [50, 200, 500, true],
|
||||||
paginationCounter: "rows",
|
paginationCounter: "rows",
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@@ -235,21 +235,48 @@ const requestsTable = new Tabulator("#requestsTable", {
|
|||||||
{title: "Ист.", field: "source_id", width: 55, formatter: sourceFormatter},
|
{title: "Ист.", field: "source_id", width: 55, formatter: sourceFormatter},
|
||||||
{title: "Спутник", field: "satellite_name", width: 100},
|
{title: "Спутник", field: "satellite_name", width: 100},
|
||||||
{title: "Статус", field: "status", width: 105, formatter: statusFormatter},
|
{title: "Статус", field: "status", width: 105, formatter: statusFormatter},
|
||||||
{title: "Приоритет", field: "priority", width: 85, formatter: priorityFormatter},
|
{title: "Приоритет", field: "priority", width: 105, formatter: priorityFormatter},
|
||||||
{title: "Заявка", field: "request_date", width: 85},
|
{title: "Заявка", field: "request_date_display", width: 105,
|
||||||
{title: "Карточка", field: "card_date", width: 85},
|
sorter: function(a, b, aRow, bRow) {
|
||||||
{title: "Планирование", field: "planned_at", width: 120},
|
const dateA = aRow.getData().request_date;
|
||||||
|
const dateB = bRow.getData().request_date;
|
||||||
|
if (!dateA && !dateB) return 0;
|
||||||
|
if (!dateA) return 1;
|
||||||
|
if (!dateB) return -1;
|
||||||
|
return new Date(dateA) - new Date(dateB);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{title: "Карточка", field: "card_date_display", width: 120,
|
||||||
|
sorter: function(a, b, aRow, bRow) {
|
||||||
|
const dateA = aRow.getData().card_date;
|
||||||
|
const dateB = bRow.getData().card_date;
|
||||||
|
if (!dateA && !dateB) return 0;
|
||||||
|
if (!dateA) return 1;
|
||||||
|
if (!dateB) return -1;
|
||||||
|
return new Date(dateA) - new Date(dateB);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{title: "Планирование", field: "planned_at_display", width: 150,
|
||||||
|
sorter: function(a, b, aRow, bRow) {
|
||||||
|
const dateA = aRow.getData().planned_at;
|
||||||
|
const dateB = bRow.getData().planned_at;
|
||||||
|
if (!dateA && !dateB) return 0;
|
||||||
|
if (!dateA) return 1;
|
||||||
|
if (!dateB) return -1;
|
||||||
|
return new Date(dateA) - new Date(dateB);
|
||||||
|
}
|
||||||
|
},
|
||||||
{title: "Down", field: "downlink", width: 65, hozAlign: "right", formatter: function(cell) { return numberFormatter(cell, 2); }},
|
{title: "Down", field: "downlink", width: 65, hozAlign: "right", formatter: function(cell) { return numberFormatter(cell, 2); }},
|
||||||
{title: "Up", field: "uplink", width: 65, hozAlign: "right", formatter: function(cell) { return numberFormatter(cell, 2); }},
|
{title: "Up", field: "uplink", width: 65, hozAlign: "right", formatter: function(cell) { return numberFormatter(cell, 2); }},
|
||||||
{title: "Пер.", field: "transfer", width: 50, hozAlign: "right", formatter: function(cell) { return numberFormatter(cell, 0); }},
|
{title: "Пер.", field: "transfer", width: 50, hozAlign: "right", formatter: function(cell) { return numberFormatter(cell, 0); }},
|
||||||
{title: "Коорд. ГСО", field: "coords_lat", width: 110, formatter: coordsFormatter},
|
{title: "Коорд. ГСО", field: "coords_lat", width: 130, formatter: coordsFormatter},
|
||||||
{title: "Район", field: "region", width: 80, formatter: function(cell) {
|
{title: "Район", field: "region", width: 100, formatter: function(cell) {
|
||||||
const val = cell.getValue();
|
const val = cell.getValue();
|
||||||
return val ? val.substring(0, 12) + (val.length > 12 ? '...' : '') : '-';
|
return val ? val.substring(0, 12) + (val.length > 12 ? '...' : '') : '-';
|
||||||
}},
|
}},
|
||||||
{title: "ГСО", field: "gso_success", width: 50, hozAlign: "center", formatter: boolFormatter},
|
{title: "ГСО", field: "gso_success", width: 50, hozAlign: "center", formatter: boolFormatter},
|
||||||
{title: "Куб", field: "kubsat_success", width: 50, hozAlign: "center", formatter: boolFormatter},
|
{title: "Куб", field: "kubsat_success", width: 50, hozAlign: "center", formatter: boolFormatter},
|
||||||
{title: "Коорд. ист.", field: "coords_source_lat", width: 110, formatter: coordsFormatter},
|
{title: "Коорд. ист.", field: "coords_source_lat", width: 140, formatter: coordsFormatter},
|
||||||
{title: "Комментарий", field: "comment", width: 180, formatter: commentFormatter},
|
{title: "Комментарий", field: "comment", width: 180, formatter: commentFormatter},
|
||||||
{title: "Действия", field: "id", width: 105, formatter: actionsFormatter, headerSort: false},
|
{title: "Действия", field: "id", width: 105, formatter: actionsFormatter, headerSort: false},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -113,6 +113,19 @@
|
|||||||
.card-body canvas:active {
|
.card-body canvas:active {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Summary rows in satellite table */
|
||||||
|
.table-warning.fw-bold td {
|
||||||
|
border-bottom: 2px solid #ffc107 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-info.fw-bold td {
|
||||||
|
border-bottom: 2px solid #0dcaf0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.satellite-stat-row:hover {
|
||||||
|
background-color: #f8f9fa !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -326,6 +339,17 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
<!-- Total summary rows -->
|
||||||
|
<tr class="table-info">
|
||||||
|
<td class="text-left"><strong>Всего </strong></td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge bg-warning text-dark fs-6">{{ total_points }}</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge bg-warning text-dark fs-6">{{ total_sources }}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!-- Individual satellite stats -->
|
||||||
{% for stat in satellite_stats %}
|
{% for stat in satellite_stats %}
|
||||||
<tr class="satellite-stat-row">
|
<tr class="satellite-stat-row">
|
||||||
<td>{{ stat.parameter_obj__id_satellite__name }}</td>
|
<td>{{ stat.parameter_obj__id_satellite__name }}</td>
|
||||||
@@ -373,6 +397,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Source Objects Charts -->
|
||||||
|
<div class="row mb-4 stats-block" id="source-charts-block">
|
||||||
|
<div class="col-md-6 stats-block" id="source-pie-chart-block">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<i class="bi bi-pie-chart"></i> Распределение объектов по спутникам
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<canvas id="sourcePieChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 stats-block" id="source-bar-chart-block">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<i class="bi bi-bar-chart"></i> Выбранные спутники по количеству объектов
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<canvas id="sourceBarChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Floating Settings Button -->
|
<!-- Floating Settings Button -->
|
||||||
<div class="floating-settings">
|
<div class="floating-settings">
|
||||||
<button type="button" class="btn settings-btn" data-bs-toggle="modal" data-bs-target="#settingsModal" title="Настройки отображения">
|
<button type="button" class="btn settings-btn" data-bs-toggle="modal" data-bs-target="#settingsModal" title="Настройки отображения">
|
||||||
@@ -468,7 +516,21 @@
|
|||||||
<input class="form-check-input" type="checkbox" id="show-bar-chart" checked>
|
<input class="form-check-input" type="checkbox" id="show-bar-chart" checked>
|
||||||
<label class="form-check-label" for="show-bar-chart">
|
<label class="form-check-label" for="show-bar-chart">
|
||||||
<i class="bi bi-bar-chart text-secondary"></i>
|
<i class="bi bi-bar-chart text-secondary"></i>
|
||||||
Столбчатая диаграмма топ-10
|
Столбчатая диаграмма точек по спутникам
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input class="form-check-input" type="checkbox" id="show-source-pie-chart" checked>
|
||||||
|
<label class="form-check-label" for="show-source-pie-chart">
|
||||||
|
<i class="bi bi-pie-chart text-success"></i>
|
||||||
|
Круговая диаграмма объектов по спутникам
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input class="form-check-input" type="checkbox" id="show-source-bar-chart" checked>
|
||||||
|
<label class="form-check-label" for="show-source-bar-chart">
|
||||||
|
<i class="bi bi-bar-chart text-warning"></i>
|
||||||
|
Столбчатая диаграмма объектов по спутникам
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -588,7 +650,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const dailyLabels = dailyData.map(d => {
|
const dailyLabels = dailyData.map(d => {
|
||||||
if (d.date) {
|
if (d.date) {
|
||||||
const date = new Date(d.date);
|
const date = new Date(d.date);
|
||||||
return date.toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit', year:"2-digit" });
|
const dateStr = date.toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit', year:"2-digit" });
|
||||||
|
const dayStr = date.toLocaleDateString('ru-RU', { weekday: 'short' });
|
||||||
|
return dateStr + ' (' + dayStr + ')';
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
@@ -743,6 +807,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
barChart.data.datasets[0].backgroundColor = barColors;
|
barChart.data.datasets[0].backgroundColor = barColors;
|
||||||
barChart.update();
|
barChart.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update source charts
|
||||||
|
updateSourceCharts();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize charts
|
// Initialize charts
|
||||||
@@ -829,10 +896,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
color: '#333',
|
color: '#333',
|
||||||
font: {
|
font: {
|
||||||
weight: 'bold',
|
weight: 'bold',
|
||||||
size: 11
|
size: 12
|
||||||
},
|
},
|
||||||
formatter: function(value) {
|
formatter: function(value, context) {
|
||||||
return value;
|
const total = context.dataset.data.reduce((a, b) => a + b, 0);
|
||||||
|
const percentage = ((value / total) * 100).toFixed(1);
|
||||||
|
return percentage + '%\n(' + value + ')';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -890,10 +959,149 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Source object charts (similar to satellite charts but for sources count)
|
||||||
|
let sourcePieChart = null;
|
||||||
|
let sourceBarChart = null;
|
||||||
|
|
||||||
|
// Initialize source charts
|
||||||
|
function initSourceCharts() {
|
||||||
|
const filteredStats = getFilteredSatelliteData();
|
||||||
|
|
||||||
|
// Source Pie Chart
|
||||||
|
if (filteredStats.length > 0) {
|
||||||
|
const sourcePieLabels = filteredStats.map(s => s.parameter_obj__id_satellite__name);
|
||||||
|
const sourcePieData = filteredStats.map(s => s.sources_count);
|
||||||
|
const sourcePieColors = filteredStats.map((s, i) => colors[satelliteStats.indexOf(s) % colors.length]);
|
||||||
|
|
||||||
|
sourcePieChart = new Chart(document.getElementById('sourcePieChart'), {
|
||||||
|
type: 'doughnut',
|
||||||
|
data: {
|
||||||
|
labels: sourcePieLabels,
|
||||||
|
datasets: [{
|
||||||
|
data: sourcePieData,
|
||||||
|
backgroundColor: sourcePieColors
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'right',
|
||||||
|
labels: {
|
||||||
|
boxWidth: 12,
|
||||||
|
font: {
|
||||||
|
size: 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
datalabels: {
|
||||||
|
color: '#fff',
|
||||||
|
font: {
|
||||||
|
weight: 'bold',
|
||||||
|
size: 10
|
||||||
|
},
|
||||||
|
formatter: function(value, context) {
|
||||||
|
const total = context.dataset.data.reduce((a, b) => a + b, 0);
|
||||||
|
const percentage = ((value / total) * 100).toFixed(1);
|
||||||
|
if (percentage < 3) return '';
|
||||||
|
return value + '\n(' + percentage + '%)';
|
||||||
|
},
|
||||||
|
textAlign: 'center'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source Bar Chart (top 15)
|
||||||
|
if (filteredStats.length > 0) {
|
||||||
|
const topSourceStats = filteredStats.slice(0, 15);
|
||||||
|
const sourceBarLabels = topSourceStats.map(s => s.parameter_obj__id_satellite__name);
|
||||||
|
const sourceBarData = topSourceStats.map(s => s.sources_count);
|
||||||
|
const sourceBarColors = topSourceStats.map((s, i) => colors[satelliteStats.indexOf(s) % colors.length]);
|
||||||
|
|
||||||
|
sourceBarChart = new Chart(document.getElementById('sourceBarChart'), {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: sourceBarLabels,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Количество объектов',
|
||||||
|
data: sourceBarData,
|
||||||
|
backgroundColor: sourceBarColors
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
indexAxis: 'y',
|
||||||
|
interaction: {
|
||||||
|
intersect: false,
|
||||||
|
mode: 'index'
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
datalabels: {
|
||||||
|
anchor: 'end',
|
||||||
|
align: 'end',
|
||||||
|
color: '#333',
|
||||||
|
font: {
|
||||||
|
weight: 'bold',
|
||||||
|
size: 12
|
||||||
|
},
|
||||||
|
formatter: function(value, context) {
|
||||||
|
const total = context.dataset.data.reduce((a, b) => a + b, 0);
|
||||||
|
const percentage = ((value / total) * 100).toFixed(1);
|
||||||
|
return percentage + '%\n(' + value + ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
beginAtZero: true,
|
||||||
|
grace: '10%'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update source charts based on satellite selection
|
||||||
|
function updateSourceCharts() {
|
||||||
|
const filteredStats = getFilteredSatelliteData();
|
||||||
|
|
||||||
|
// Update source pie chart
|
||||||
|
if (sourcePieChart) {
|
||||||
|
const sourcePieLabels = filteredStats.map(s => s.parameter_obj__id_satellite__name);
|
||||||
|
const sourcePieData = filteredStats.map(s => s.sources_count);
|
||||||
|
const sourcePieColors = filteredStats.map((s, i) => colors[satelliteStats.indexOf(s) % colors.length]);
|
||||||
|
|
||||||
|
sourcePieChart.data.labels = sourcePieLabels;
|
||||||
|
sourcePieChart.data.datasets[0].data = sourcePieData;
|
||||||
|
sourcePieChart.data.datasets[0].backgroundColor = sourcePieColors;
|
||||||
|
sourcePieChart.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update source bar chart (limit to top 15 for readability)
|
||||||
|
if (sourceBarChart) {
|
||||||
|
const topSourceStats = filteredStats.slice(0, 15);
|
||||||
|
const sourceBarLabels = topSourceStats.map(s => s.parameter_obj__id_satellite__name);
|
||||||
|
const sourceBarData = topSourceStats.map(s => s.sources_count);
|
||||||
|
const sourceBarColors = topSourceStats.map((s, i) => colors[satelliteStats.indexOf(s) % colors.length]);
|
||||||
|
|
||||||
|
sourceBarChart.data.labels = sourceBarLabels;
|
||||||
|
sourceBarChart.data.datasets[0].data = sourceBarData;
|
||||||
|
sourceBarChart.data.datasets[0].backgroundColor = sourceBarColors;
|
||||||
|
sourceBarChart.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize satellite functionality
|
// Initialize satellite functionality
|
||||||
initSatelliteCheckboxes();
|
initSatelliteCheckboxes();
|
||||||
initSatelliteButtons();
|
initSatelliteButtons();
|
||||||
initCharts();
|
initCharts();
|
||||||
|
initSourceCharts();
|
||||||
|
|
||||||
// Note: Zoom functionality temporarily disabled due to plugin loading issues
|
// Note: Zoom functionality temporarily disabled due to plugin loading issues
|
||||||
// Can be re-enabled when zoom plugin is properly configured
|
// Can be re-enabled when zoom plugin is properly configured
|
||||||
@@ -913,9 +1121,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
'show-daily-chart': 'daily-chart-block',
|
'show-daily-chart': 'daily-chart-block',
|
||||||
'show-satellite-table': 'satellite-table-block',
|
'show-satellite-table': 'satellite-table-block',
|
||||||
|
|
||||||
// Individual charts
|
// Satellite charts
|
||||||
'show-pie-chart': 'pie-chart-block',
|
'show-pie-chart': 'pie-chart-block',
|
||||||
'show-bar-chart': 'bar-chart-block'
|
'show-bar-chart': 'bar-chart-block',
|
||||||
|
|
||||||
|
// Source object charts
|
||||||
|
'show-source-pie-chart': 'source-pie-chart-block',
|
||||||
|
'show-source-bar-chart': 'source-bar-chart-block'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load settings from localStorage
|
// Load settings from localStorage
|
||||||
@@ -997,6 +1209,18 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
satelliteChartsBlock.classList.add('hidden');
|
satelliteChartsBlock.classList.add('hidden');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Source charts block
|
||||||
|
const sourceChartsVisible = ['show-source-pie-chart', 'show-source-bar-chart']
|
||||||
|
.some(id => !document.getElementById(blockSettings[id])?.classList.contains('hidden'));
|
||||||
|
const sourceChartsBlock = document.getElementById('source-charts-block');
|
||||||
|
if (sourceChartsBlock) {
|
||||||
|
if (sourceChartsVisible) {
|
||||||
|
sourceChartsBlock.classList.remove('hidden');
|
||||||
|
} else {
|
||||||
|
sourceChartsBlock.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize settings
|
// Initialize settings
|
||||||
@@ -1044,7 +1268,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
if (selectChartsOnlyBtn) {
|
if (selectChartsOnlyBtn) {
|
||||||
selectChartsOnlyBtn.addEventListener('click', function() {
|
selectChartsOnlyBtn.addEventListener('click', function() {
|
||||||
const chartKeys = ['show-daily-chart', 'show-satellite-table', 'show-pie-chart', 'show-bar-chart'];
|
const chartKeys = ['show-daily-chart', 'show-satellite-table', 'show-pie-chart', 'show-bar-chart', 'show-source-pie-chart', 'show-source-bar-chart'];
|
||||||
Object.keys(blockSettings).forEach(key => {
|
Object.keys(blockSettings).forEach(key => {
|
||||||
currentSettings[key] = chartKeys.includes(key);
|
currentSettings[key] = chartKeys.includes(key);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -80,8 +80,17 @@ class KubsatView(LoginRequiredMixin, FormView):
|
|||||||
|
|
||||||
# Сериализуем заявки в JSON для Tabulator
|
# Сериализуем заявки в JSON для Tabulator
|
||||||
import json
|
import json
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
requests_json_data = []
|
requests_json_data = []
|
||||||
for req in requests_list:
|
for req in requests_list:
|
||||||
|
# Конвертируем даты в локальный часовой пояс для отображения
|
||||||
|
planned_at_local = None
|
||||||
|
planned_at_iso = None
|
||||||
|
if req.planned_at:
|
||||||
|
planned_at_local = timezone.localtime(req.planned_at)
|
||||||
|
planned_at_iso = planned_at_local.isoformat()
|
||||||
|
|
||||||
requests_json_data.append({
|
requests_json_data.append({
|
||||||
'id': req.id,
|
'id': req.id,
|
||||||
'source_id': req.source_id,
|
'source_id': req.source_id,
|
||||||
@@ -90,9 +99,18 @@ class KubsatView(LoginRequiredMixin, FormView):
|
|||||||
'status_display': req.get_status_display(),
|
'status_display': req.get_status_display(),
|
||||||
'priority': req.priority,
|
'priority': req.priority,
|
||||||
'priority_display': req.get_priority_display(),
|
'priority_display': req.get_priority_display(),
|
||||||
'request_date': req.request_date.strftime('%d.%m.%Y') if req.request_date else '-',
|
# Даты в ISO формате для правильной сортировки
|
||||||
'card_date': req.card_date.strftime('%d.%m.%Y') if req.card_date else '-',
|
'request_date': req.request_date.isoformat() if req.request_date else None,
|
||||||
'planned_at': req.planned_at.strftime('%d.%m.%Y %H:%M') if req.planned_at else '-',
|
'card_date': req.card_date.isoformat() if req.card_date else None,
|
||||||
|
'planned_at': planned_at_iso,
|
||||||
|
# Отформатированные даты для отображения
|
||||||
|
'request_date_display': req.request_date.strftime('%d.%m.%Y') if req.request_date else '-',
|
||||||
|
'card_date_display': req.card_date.strftime('%d.%m.%Y') if req.card_date else '-',
|
||||||
|
'planned_at_display': (
|
||||||
|
planned_at_local.strftime('%d.%m.%Y') if planned_at_local and planned_at_local.hour == 0 and planned_at_local.minute == 0
|
||||||
|
else planned_at_local.strftime('%d.%m.%Y %H:%M') if planned_at_local
|
||||||
|
else '-'
|
||||||
|
),
|
||||||
'downlink': float(req.downlink) if req.downlink else None,
|
'downlink': float(req.downlink) if req.downlink else None,
|
||||||
'uplink': float(req.uplink) if req.uplink else None,
|
'uplink': float(req.uplink) if req.uplink else None,
|
||||||
'transfer': float(req.transfer) if req.transfer else None,
|
'transfer': float(req.transfer) if req.transfer else None,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from django.views import View
|
|||||||
from django.views.generic import ListView, CreateView, UpdateView
|
from django.views.generic import ListView, CreateView, UpdateView
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
from mainapp.models import SourceRequest, SourceRequestStatusHistory, Source, Satellite
|
from mainapp.models import SourceRequest, SourceRequestStatusHistory, Source, Satellite
|
||||||
from mainapp.forms import SourceRequestForm
|
from mainapp.forms import SourceRequestForm
|
||||||
@@ -200,7 +201,7 @@ class SourceRequestBulkDeleteView(LoginRequiredMixin, View):
|
|||||||
|
|
||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
'success': True,
|
'success': True,
|
||||||
'message': f'Удалено заявок: {deleted_count}',
|
'message': 'Заявки удалены',
|
||||||
'deleted_count': deleted_count
|
'deleted_count': deleted_count
|
||||||
})
|
})
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
@@ -292,7 +293,14 @@ class SourceRequestExportView(LoginRequiredMixin, View):
|
|||||||
ws.cell(row=row_num, column=2, value=req.card_date.strftime('%d.%m.%Y') if req.card_date else '')
|
ws.cell(row=row_num, column=2, value=req.card_date.strftime('%d.%m.%Y') if req.card_date else '')
|
||||||
|
|
||||||
# Дата проведения
|
# Дата проведения
|
||||||
ws.cell(row=row_num, column=3, value=req.planned_at.strftime('%d.%m.%y %H:%M') if req.planned_at else '')
|
planned_at_local = timezone.localtime(req.planned_at) if req.planned_at else None
|
||||||
|
planned_at_str = ''
|
||||||
|
if planned_at_local:
|
||||||
|
if planned_at_local.hour == 0 and planned_at_local.minute == 0:
|
||||||
|
planned_at_str = planned_at_local.strftime('%d.%m.%y')
|
||||||
|
else:
|
||||||
|
planned_at_str = planned_at_local.strftime('%d.%m.%y %H:%M')
|
||||||
|
ws.cell(row=row_num, column=3, value=planned_at_str)
|
||||||
|
|
||||||
# Спутник
|
# Спутник
|
||||||
satellite_str = ''
|
satellite_str = ''
|
||||||
@@ -413,7 +421,7 @@ class SourceRequestAPIView(LoginRequiredMixin, View):
|
|||||||
history.append({
|
history.append({
|
||||||
'old_status': h.get_old_status_display() if h.old_status else '-',
|
'old_status': h.get_old_status_display() if h.old_status else '-',
|
||||||
'new_status': h.get_new_status_display(),
|
'new_status': h.get_new_status_display(),
|
||||||
'changed_at': h.changed_at.strftime('%d.%m.%Y %H:%M') if h.changed_at else '-',
|
'changed_at': timezone.localtime(h.changed_at).strftime('%d.%m.%Y %H:%M') if h.changed_at else '-',
|
||||||
'changed_by': str(h.changed_by) if h.changed_by else '-',
|
'changed_by': str(h.changed_by) if h.changed_by else '-',
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -423,13 +431,18 @@ class SourceRequestAPIView(LoginRequiredMixin, View):
|
|||||||
'status_display': req.get_status_display(),
|
'status_display': req.get_status_display(),
|
||||||
'priority': req.priority,
|
'priority': req.priority,
|
||||||
'priority_display': req.get_priority_display(),
|
'priority_display': req.get_priority_display(),
|
||||||
'planned_at': req.planned_at.strftime('%d.%m.%Y %H:%M') if req.planned_at else '-',
|
'planned_at': (
|
||||||
|
timezone.localtime(req.planned_at).strftime('%d.%m.%Y')
|
||||||
|
if req.planned_at and timezone.localtime(req.planned_at).hour == 0 and timezone.localtime(req.planned_at).minute == 0
|
||||||
|
else timezone.localtime(req.planned_at).strftime('%d.%m.%Y %H:%M') if req.planned_at
|
||||||
|
else '-'
|
||||||
|
),
|
||||||
'request_date': req.request_date.strftime('%d.%m.%Y') if req.request_date else '-',
|
'request_date': req.request_date.strftime('%d.%m.%Y') if req.request_date else '-',
|
||||||
'status_updated_at': req.status_updated_at.strftime('%d.%m.%Y %H:%M') if req.status_updated_at else '-',
|
'status_updated_at': timezone.localtime(req.status_updated_at).strftime('%d.%m.%Y %H:%M') if req.status_updated_at else '-',
|
||||||
'gso_success': req.gso_success,
|
'gso_success': req.gso_success,
|
||||||
'kubsat_success': req.kubsat_success,
|
'kubsat_success': req.kubsat_success,
|
||||||
'comment': req.comment or '-',
|
'comment': req.comment or '-',
|
||||||
'created_at': req.created_at.strftime('%d.%m.%Y %H:%M') if req.created_at else '-',
|
'created_at': timezone.localtime(req.created_at).strftime('%d.%m.%Y %H:%M') if req.created_at else '-',
|
||||||
'created_by': str(req.created_by) if req.created_by else '-',
|
'created_by': str(req.created_by) if req.created_by else '-',
|
||||||
'history': history,
|
'history': history,
|
||||||
})
|
})
|
||||||
@@ -464,7 +477,7 @@ class SourceRequestDetailAPIView(LoginRequiredMixin, View):
|
|||||||
history.append({
|
history.append({
|
||||||
'old_status': h.get_old_status_display() if h.old_status else '-',
|
'old_status': h.get_old_status_display() if h.old_status else '-',
|
||||||
'new_status': h.get_new_status_display(),
|
'new_status': h.get_new_status_display(),
|
||||||
'changed_at': h.changed_at.strftime('%d.%m.%Y %H:%M') if h.changed_at else '-',
|
'changed_at': timezone.localtime(h.changed_at).strftime('%d.%m.%Y %H:%M') if h.changed_at else '-',
|
||||||
'changed_by': str(h.changed_by) if h.changed_by else '-',
|
'changed_by': str(h.changed_by) if h.changed_by else '-',
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -499,13 +512,18 @@ class SourceRequestDetailAPIView(LoginRequiredMixin, View):
|
|||||||
'status_display': req.get_status_display(),
|
'status_display': req.get_status_display(),
|
||||||
'priority': req.priority,
|
'priority': req.priority,
|
||||||
'priority_display': req.get_priority_display(),
|
'priority_display': req.get_priority_display(),
|
||||||
'planned_at': req.planned_at.strftime('%Y-%m-%dT%H:%M') if req.planned_at else '',
|
'planned_at': timezone.localtime(req.planned_at).strftime('%Y-%m-%dT%H:%M') if req.planned_at else '',
|
||||||
'planned_at_display': req.planned_at.strftime('%d.%m.%Y %H:%M') if req.planned_at else '-',
|
'planned_at_display': (
|
||||||
|
timezone.localtime(req.planned_at).strftime('%d.%m.%Y')
|
||||||
|
if req.planned_at and timezone.localtime(req.planned_at).hour == 0 and timezone.localtime(req.planned_at).minute == 0
|
||||||
|
else timezone.localtime(req.planned_at).strftime('%d.%m.%Y %H:%M') if req.planned_at
|
||||||
|
else '-'
|
||||||
|
),
|
||||||
'request_date': req.request_date.isoformat() if req.request_date else None,
|
'request_date': req.request_date.isoformat() if req.request_date else None,
|
||||||
'request_date_display': req.request_date.strftime('%d.%m.%Y') if req.request_date else '-',
|
'request_date_display': req.request_date.strftime('%d.%m.%Y') if req.request_date else '-',
|
||||||
'card_date': req.card_date.isoformat() if req.card_date else None,
|
'card_date': req.card_date.isoformat() if req.card_date else None,
|
||||||
'card_date_display': req.card_date.strftime('%d.%m.%Y') if req.card_date else '-',
|
'card_date_display': req.card_date.strftime('%d.%m.%Y') if req.card_date else '-',
|
||||||
'status_updated_at': req.status_updated_at.strftime('%d.%m.%Y %H:%M') if req.status_updated_at else '-',
|
'status_updated_at': timezone.localtime(req.status_updated_at).strftime('%d.%m.%Y %H:%M') if req.status_updated_at else '-',
|
||||||
'downlink': req.downlink,
|
'downlink': req.downlink,
|
||||||
'uplink': req.uplink,
|
'uplink': req.uplink,
|
||||||
'transfer': req.transfer,
|
'transfer': req.transfer,
|
||||||
@@ -513,7 +531,7 @@ class SourceRequestDetailAPIView(LoginRequiredMixin, View):
|
|||||||
'gso_success': req.gso_success,
|
'gso_success': req.gso_success,
|
||||||
'kubsat_success': req.kubsat_success,
|
'kubsat_success': req.kubsat_success,
|
||||||
'comment': req.comment or '',
|
'comment': req.comment or '',
|
||||||
'created_at': req.created_at.strftime('%d.%m.%Y %H:%M') if req.created_at else '-',
|
'created_at': timezone.localtime(req.created_at).strftime('%d.%m.%Y %H:%M') if req.created_at else '-',
|
||||||
'created_by': str(req.created_by) if req.created_by else '-',
|
'created_by': str(req.created_by) if req.created_by else '-',
|
||||||
'history': history,
|
'history': history,
|
||||||
# Координаты ГСО
|
# Координаты ГСО
|
||||||
|
|||||||
1
dbapp/static/luxon/luxon.min.js
vendored
Normal file
1
dbapp/static/luxon/luxon.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user