Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 88f6b77455 | |||
| bc5e08142a | |||
| 373839cf03 | |||
| 8e31309c55 | |||
| e8391dbf1e |
+25
-9
@@ -11862,25 +11862,39 @@ def batch_upload_page():
|
||||
|
||||
return render_template('upload_batch.html')
|
||||
|
||||
|
||||
def clean_db_field(val):
|
||||
"""Bereinigt Werte, die fälschlicherweise als String-Listen aus der CSV kommen."""
|
||||
"""Bereinigt Werte, die fälschlicherweise als String-Listen oder mit Klammern aus der CSV kommen."""
|
||||
import ast
|
||||
import pandas as pd
|
||||
|
||||
if not val or pd.isna(val):
|
||||
return None
|
||||
|
||||
val_str = str(val).strip()
|
||||
|
||||
# Erkennt und entpackt String-Listen wie "['100465']" oder '["100465"]'
|
||||
if (val_str.startswith("['") and val_str.endswith("']")) or (val_str.startswith('["') and val_str.endswith('"]')):
|
||||
inner = val_str[2:-2].strip()
|
||||
return inner if inner and inner != "''" and inner != '""' else None
|
||||
# Wenn es wie eine Liste aussieht (z.B. "['100177']" oder "['']")
|
||||
if val_str.startswith("[") and val_str.endswith("]"):
|
||||
try:
|
||||
parsed = ast.literal_eval(val_str)
|
||||
if isinstance(parsed, list):
|
||||
# Nimm das erste Element der Liste, wenn vorhanden
|
||||
for item in parsed:
|
||||
cleaned_item = str(item).strip()
|
||||
if cleaned_item and cleaned_item not in ("", "''", '""', "None", "nan"):
|
||||
return cleaned_item
|
||||
return None
|
||||
except Exception:
|
||||
# Fallback bei Syntaxfehlern
|
||||
inner = val_str[1:-1].strip().replace("'", "").replace('"', '')
|
||||
return inner if inner and inner not in ("''", '""') else None
|
||||
|
||||
if val_str in ("[]", "['']", '[""]', "nan", "None"):
|
||||
if val_str in ("[]", "['']", '[""]', "nan", "None", "''", '""'):
|
||||
return None
|
||||
|
||||
return val_str
|
||||
|
||||
|
||||
|
||||
@app.route('/upload_csv_batch', methods=['POST'])
|
||||
def upload_csv_batch():
|
||||
"""
|
||||
@@ -12064,8 +12078,10 @@ def upload_csv_batch():
|
||||
|
||||
reservierbar = bool(row.get('Reservierbar', False))
|
||||
|
||||
# Code_4 Behandlung
|
||||
row_code = clean_db_field(row.get('Code_4', ''))
|
||||
# Code_4 / Barcode sauber extrahieren und bereinigen
|
||||
raw_code = row.get('Code_4') or row.get('Barcode') or ''
|
||||
row_code = clean_db_field(raw_code)
|
||||
|
||||
if row_code:
|
||||
unique_code = row_code
|
||||
elif item_count > 1:
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 1. Ein echter CSV-Parser, der Zeilenumbrüche und Kommas in Texten korrekt ignoriert
|
||||
// 1. Robuster CSV-Parser, der Zeilenumbrüche und Kommas in Texten korrekt ignoriert
|
||||
function parseCSV(csvString) {
|
||||
const rows = [];
|
||||
let currentRow = [];
|
||||
@@ -70,7 +70,6 @@ function parseCSV(csvString) {
|
||||
} else if ((char === '\n' || char === '\r') && !insideQuotes) {
|
||||
if (char === '\r' && nextChar === '\n') i++; // Windows Umbrüche überspringen
|
||||
currentRow.push(currentCell);
|
||||
// Leere Zeilen ignorieren
|
||||
if (currentRow.length > 1 || currentRow[0] !== '') {
|
||||
rows.push(currentRow);
|
||||
}
|
||||
@@ -94,7 +93,6 @@ function rowToCSV(rowArray) {
|
||||
return rowArray.map(cell => {
|
||||
if (cell === null || cell === undefined) return '';
|
||||
let cellStr = String(cell);
|
||||
// Wenn kritische Zeichen drin sind, sauber in Anführungszeichen verpacken
|
||||
if (cellStr.includes(',') || cellStr.includes('"') || cellStr.includes('\n') || cellStr.includes('\r')) {
|
||||
return '"' + cellStr.replace(/"/g, '""') + '"';
|
||||
}
|
||||
@@ -102,7 +100,7 @@ function rowToCSV(rowArray) {
|
||||
}).join(',');
|
||||
}
|
||||
|
||||
// 3. Bildnamen extrahieren (bleibt wie es war)
|
||||
// 3. Bildnamen extrahieren
|
||||
function extractImageNames(cellValue) {
|
||||
if (!cellValue) return [];
|
||||
let raw = String(cellValue).replace(/^["']|["']$/g, '').trim();
|
||||
@@ -155,11 +153,10 @@ document.getElementById('batchUploadForm').addEventListener('submit', async func
|
||||
imageMap.set(file.name.toLowerCase(), file);
|
||||
});
|
||||
|
||||
const BATCH_SIZE = 50;
|
||||
const BATCH_SIZE = 20;
|
||||
|
||||
try {
|
||||
const csvText = await csvFile.text();
|
||||
// Hier rufen wir jetzt unseren sicheren Parser auf!
|
||||
const allRows = parseCSV(csvText);
|
||||
|
||||
if (allRows.length <= 1) {
|
||||
@@ -171,10 +168,8 @@ document.getElementById('batchUploadForm').addEventListener('submit', async func
|
||||
|
||||
log(`${dataRows.length} Einträge gefunden. Bereite Batches vor...`);
|
||||
|
||||
// Spalten-Index von "Images" sicher ermitteln
|
||||
const imagesColIndex = headerRow.findIndex(h => h.toLowerCase().trim() === 'images');
|
||||
|
||||
// In Batches aufteilen
|
||||
const batches = [];
|
||||
for (let i = 0; i < dataRows.length; i += BATCH_SIZE) {
|
||||
batches.push(dataRows.slice(i, i + BATCH_SIZE));
|
||||
@@ -183,63 +178,77 @@ document.getElementById('batchUploadForm').addEventListener('submit', async func
|
||||
progressBar.max = batches.length;
|
||||
progressBar.value = 0;
|
||||
|
||||
// Sequenzieller Upload mit Fehlertoleranz pro Batch
|
||||
for (let b = 0; b < batches.length; b++) {
|
||||
const batchRows = batches[b];
|
||||
progressText.textContent = `Lade Batch ${b + 1} von ${batches.length} hoch...`;
|
||||
|
||||
const requiredImagesForBatch = new Set();
|
||||
try {
|
||||
const requiredImagesForBatch = new Set();
|
||||
|
||||
if (imagesColIndex !== -1) {
|
||||
batchRows.forEach(rowCols => {
|
||||
if (rowCols[imagesColIndex]) {
|
||||
const imgNames = extractImageNames(rowCols[imagesColIndex]);
|
||||
imgNames.forEach(name => {
|
||||
const fileMatch = imageMap.get(name.toLowerCase());
|
||||
if (fileMatch) {
|
||||
requiredImagesForBatch.add(fileMatch);
|
||||
}
|
||||
});
|
||||
if (imagesColIndex !== -1) {
|
||||
batchRows.forEach(rowCols => {
|
||||
if (rowCols[imagesColIndex]) {
|
||||
const imgNames = extractImageNames(rowCols[imagesColIndex]);
|
||||
imgNames.forEach(name => {
|
||||
const fileMatch = imageMap.get(name.toLowerCase());
|
||||
if (fileMatch) {
|
||||
requiredImagesForBatch.add(fileMatch);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const batchCsvArray = [headerRow, ...batchRows];
|
||||
const batchCsvText = batchCsvArray.map(rowToCSV).join('\n');
|
||||
const batchCsvBlob = new Blob([batchCsvText], { type: 'text/csv' });
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('csv_file', batchCsvBlob, `batch_${b + 1}.csv`);
|
||||
|
||||
if (csrfToken) {
|
||||
formData.append('csrf_token', csrfToken);
|
||||
}
|
||||
|
||||
requiredImagesForBatch.forEach(imgFile => {
|
||||
formData.append('images', imgFile);
|
||||
});
|
||||
|
||||
log(`Batch ${b + 1}: ${batchRows.length} Items & ${requiredImagesForBatch.size} zugehörige Bilder.`);
|
||||
|
||||
const response = await fetch('/upload_csv_batch', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken || ''
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Sicherer Zusammenbau des CSV-Batches
|
||||
const batchCsvArray = [headerRow, ...batchRows];
|
||||
const batchCsvText = batchCsvArray.map(rowToCSV).join('\n');
|
||||
const batchCsvBlob = new Blob([batchCsvText], { type: 'text/csv' });
|
||||
const responseText = await response.text();
|
||||
let result;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('csv_file', batchCsvBlob, `batch_${b + 1}.csv`);
|
||||
|
||||
if (csrfToken) {
|
||||
formData.append('csrf_token', csrfToken);
|
||||
}
|
||||
|
||||
requiredImagesForBatch.forEach(imgFile => {
|
||||
formData.append('images', imgFile);
|
||||
});
|
||||
|
||||
log(`Batch ${b + 1}: ${batchRows.length} Items & ${requiredImagesForBatch.size} zugehörige Bilder.`);
|
||||
|
||||
const response = await fetch('/upload_csv_batch', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken || ''
|
||||
try {
|
||||
result = JSON.parse(responseText);
|
||||
} catch (parseErr) {
|
||||
throw new Error(`Server-Fehler (Status ${response.status}). HTML statt JSON erhalten.`);
|
||||
}
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (!response.ok || !result.success) {
|
||||
throw new Error(result.message || `Server-Fehler ${response.status}`);
|
||||
}
|
||||
|
||||
if (!response.ok || !result.success) {
|
||||
throw new Error(result.message || `Server-Fehler ${response.status}`);
|
||||
log(`Batch ${b + 1} erfolgreich abgeschlossen.`);
|
||||
|
||||
} catch (batchErr) {
|
||||
log(`⚠️ FEHLER in Batch ${b + 1}: ${batchErr.message}. Überspringe und fahre fort...`);
|
||||
console.error(`Batch ${b + 1} fehlgeschlagen:`, batchErr);
|
||||
}
|
||||
|
||||
log(`Batch ${b + 1} abgeschlossen.`);
|
||||
progressBar.value = b + 1;
|
||||
}
|
||||
|
||||
progressText.textContent = "Upload erfolgreich beendet! Keine verschobenen Spalten mehr.";
|
||||
progressText.textContent = "Upload-Prozess beendet!";
|
||||
uploadBtn.disabled = false;
|
||||
|
||||
} catch (err) {
|
||||
|
||||
+8
-1
@@ -201,6 +201,7 @@ sys.path.insert(0, "/app/Web")
|
||||
from Web.modules.database import settings
|
||||
from pymongo import MongoClient
|
||||
import Web.modules.inventarsystem.data_protection as dp
|
||||
import Web.modules.database.user as us
|
||||
|
||||
tenant_id = sys.argv[1].lower()
|
||||
mode = sys.argv[2]
|
||||
@@ -245,7 +246,7 @@ page_permissions = {
|
||||
"manage_locations": True,
|
||||
}
|
||||
|
||||
if db.users.count_documents({"Username": dp.encrypt_text("admin")}) == 0:
|
||||
if db.users.count_documents({"username": dp.encrypt_text("admin")}) == 0:
|
||||
db.users.insert_one({
|
||||
"Username": dp.encrypt_text("admin"),
|
||||
"Password": hashed_pw_string,
|
||||
@@ -258,6 +259,12 @@ if db.users.count_documents({"Username": dp.encrypt_text("admin")}) == 0:
|
||||
"ActionPermissions": action_permissions,
|
||||
"PagePermissions": page_permissions,
|
||||
})
|
||||
print("User correctly added")
|
||||
|
||||
if db.users.count_documents({"Username": dp.encrypt_text("admin")}) == 0:
|
||||
us.add_user("admin", "admin123", "admin", "admin", permission_preset='full_access')
|
||||
us.make_admin("admin")
|
||||
print("Fallback successfully applied")
|
||||
|
||||
if mode == "trial":
|
||||
db.settings.update_one(
|
||||
|
||||
Reference in New Issue
Block a user