export check
parent
270fa3ace5
commit
9360a1a671
44
app.py
44
app.py
|
|
@ -1,4 +1,4 @@
|
||||||
from fastapi import FastAPI, HTTPException, File, UploadFile
|
from fastapi import FastAPI, HTTPException, File, UploadFile, Query
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import datetime
|
import datetime
|
||||||
|
|
@ -166,12 +166,20 @@ def delete_install(install_id: int):
|
||||||
|
|
||||||
# --- CSV Export/Import API ---
|
# --- CSV Export/Import API ---
|
||||||
@app.get("/api/export/csv")
|
@app.get("/api/export/csv")
|
||||||
async def export_csv():
|
async def export_csv(folder_id: int | None = Query(None, description="ID папки для экспорта, если None - экспортировать все папки")):
|
||||||
cursor.execute("""
|
if folder_id:
|
||||||
SELECT i.rust_id, i.computer_name, i.install_time, f.name as folder_name, i.protocol
|
cursor.execute("""
|
||||||
FROM installs i
|
SELECT i.rust_id, i.computer_name, i.install_time, f.name as folder_name, i.protocol
|
||||||
LEFT JOIN folders f ON i.folder_id = f.id
|
FROM installs i
|
||||||
""")
|
LEFT JOIN folders f ON i.folder_id = f.id
|
||||||
|
WHERE i.folder_id = ?
|
||||||
|
""", (folder_id,))
|
||||||
|
else:
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT i.rust_id, i.computer_name, i.install_time, f.name as folder_name, i.protocol
|
||||||
|
FROM installs i
|
||||||
|
LEFT JOIN folders f ON i.folder_id = f.id
|
||||||
|
""")
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
|
|
||||||
output = io.StringIO()
|
output = io.StringIO()
|
||||||
|
|
@ -187,23 +195,33 @@ async def export_csv():
|
||||||
return StreamingResponse(iter([output.getvalue()]), headers=headers)
|
return StreamingResponse(iter([output.getvalue()]), headers=headers)
|
||||||
|
|
||||||
@app.post("/api/import/csv")
|
@app.post("/api/import/csv")
|
||||||
async def import_csv(file: UploadFile = File(...)):
|
async def import_csv(file: UploadFile = File(...), folder_id: int | None = Query(None, description="ID папки для импорта, если None - использовать 'Несортированные'")):
|
||||||
try:
|
try:
|
||||||
contents = await file.read()
|
contents = await file.read()
|
||||||
csv_data = io.StringIO(contents.decode('utf-8'))
|
csv_data = io.StringIO(contents.decode('utf-8'))
|
||||||
reader = csv.DictReader(csv_data)
|
reader = csv.DictReader(csv_data)
|
||||||
|
|
||||||
|
target_folder_id = folder_id if folder_id is not None else unsorted_folder_id
|
||||||
|
|
||||||
for row in reader:
|
for row in reader:
|
||||||
rust_id = row['ID подключения']
|
rust_id = row['ID подключения']
|
||||||
computer_name = row['Имя компьютера']
|
computer_name = row['Имя компьютера']
|
||||||
install_time = row['Время установки']
|
install_time = row['Время установки']
|
||||||
folder_name = row['Папка']
|
folder_name = row.get('Папка', None)
|
||||||
protocol = row['Протокол'] or 'rustdesk'
|
protocol = row.get('Протокол', 'rustdesk')
|
||||||
|
|
||||||
|
# Проверяем, существует ли запись с таким rust_id
|
||||||
|
cursor.execute("SELECT id FROM installs WHERE rust_id = ?", (rust_id,))
|
||||||
|
if cursor.fetchone():
|
||||||
|
continue # Пропускаем дублирующуюся запись
|
||||||
|
|
||||||
# Получаем или создаем ID папки
|
# Получаем или создаем ID папки
|
||||||
cursor.execute("SELECT id FROM folders WHERE name = ?", (folder_name,))
|
if folder_name:
|
||||||
folder = cursor.fetchone()
|
cursor.execute("SELECT id FROM folders WHERE name = ?", (folder_name,))
|
||||||
folder_id = folder[0] if folder else unsorted_folder_id
|
folder = cursor.fetchone()
|
||||||
|
folder_id = folder[0] if folder else unsorted_folder_id
|
||||||
|
else:
|
||||||
|
folder_id = target_folder_id
|
||||||
|
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
INSERT INTO installs (rust_id, computer_name, install_time, folder_id, protocol)
|
INSERT INTO installs (rust_id, computer_name, install_time, folder_id, protocol)
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
#import-file { display: none; }
|
#import-file { display: none; }
|
||||||
#import-label { cursor: pointer; background: #007bff; color: white; padding: 5px 10px; border-radius: 5px; }
|
#import-label { cursor: pointer; background: #007bff; color: white; padding: 5px 10px; border-radius: 5px; }
|
||||||
#import-label:hover { background: #0056b3; }
|
#import-label:hover { background: #0056b3; }
|
||||||
|
.folder-select { margin: 5px 0; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -46,6 +47,9 @@
|
||||||
<label for="search-all">Все папки</label>
|
<label for="search-all">Все папки</label>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 10px;">
|
<div style="margin-top: 10px;">
|
||||||
|
<select id="folder-select" class="folder-select">
|
||||||
|
<option value="">Все папки</option>
|
||||||
|
</select>
|
||||||
<button onclick="exportCSV()">Экспорт CSV</button>
|
<button onclick="exportCSV()">Экспорт CSV</button>
|
||||||
<form id="import-form" enctype="multipart/form-data">
|
<form id="import-form" enctype="multipart/form-data">
|
||||||
<input type="file" id="import-file" accept=".csv" onchange="importCSV(this.files[0])">
|
<input type="file" id="import-file" accept=".csv" onchange="importCSV(this.files[0])">
|
||||||
|
|
@ -108,6 +112,13 @@
|
||||||
const unsortedFolder = folders.find(f => f.name === 'Несортированные');
|
const unsortedFolder = folders.find(f => f.name === 'Несортированные');
|
||||||
const unsortedFolderId = unsortedFolder ? unsortedFolder.id : null;
|
const unsortedFolderId = unsortedFolder ? unsortedFolder.id : null;
|
||||||
|
|
||||||
|
// Заполняем выпадающий список папок
|
||||||
|
const $folderSelect = $('#folder-select');
|
||||||
|
$folderSelect.append('<option value="">Все папки</option>');
|
||||||
|
folders.forEach(folder => {
|
||||||
|
$folderSelect.append(`<option value="${folder.id}">${folder.name}</option>`);
|
||||||
|
});
|
||||||
|
|
||||||
// Инициализация дерева папок
|
// Инициализация дерева папок
|
||||||
$('#folder-tree').jstree({
|
$('#folder-tree').jstree({
|
||||||
'core': {
|
'core': {
|
||||||
|
|
@ -357,14 +368,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportCSV() {
|
function exportCSV() {
|
||||||
window.location.href = `${API_URL}/export/csv`;
|
const folderId = $('#folder-select').val() || '';
|
||||||
|
window.location.href = `${API_URL}/export/csv?folder_id=${folderId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function importCSV(file) {
|
function importCSV(file) {
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
|
const folderId = $('#folder-select').val() || '';
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
|
if (folderId) {
|
||||||
|
formData.append('folder_id', folderId);
|
||||||
|
}
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: `${API_URL}/import/csv`,
|
url: `${API_URL}/import/csv`,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue