image cad area size
This commit is contained in:
@@ -0,0 +1,655 @@
|
||||
<?php include('include/headscript.php'); ?>
|
||||
<?php
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$iduser = $iduserlogin ?? null;
|
||||
|
||||
if ($iduser === null) {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT *
|
||||
FROM cad_area_jobs
|
||||
ORDER BY id DESC
|
||||
");
|
||||
$stmt->execute();
|
||||
} else {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT *
|
||||
FROM cad_area_jobs
|
||||
WHERE iduser = :iduser
|
||||
ORDER BY id DESC
|
||||
");
|
||||
$stmt->execute([
|
||||
':iduser' => $iduser
|
||||
]);
|
||||
}
|
||||
$jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="it">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||
<?php include('cssinclude.php'); ?>
|
||||
<title>Calcolo Area CAD - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-size: 1.05rem;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.back-dashboard {
|
||||
background-color: #cfe3ff !important;
|
||||
color: #1f2d3d !important;
|
||||
border: 1px solid #bcd4f4 !important;
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
padding: 10px 18px;
|
||||
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.back-dashboard:hover {
|
||||
background-color: #b9d3ff !important;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.btn-add {
|
||||
background-color: #0d6efd;
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 10px 20px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.btn-add:hover {
|
||||
background-color: #0b5ed7;
|
||||
color: #fff;
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.table thead {
|
||||
background-color: #cfe3ff;
|
||||
color: #1f2d3d;
|
||||
}
|
||||
|
||||
.drop-zone {
|
||||
border: 2px dashed #9fc5f8;
|
||||
background: #f4f8ff;
|
||||
border-radius: 16px;
|
||||
padding: 38px 20px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.drop-zone:hover,
|
||||
.drop-zone.dragover {
|
||||
background: #e8f2ff;
|
||||
border-color: #0d6efd;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.drop-zone-icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.file-preview {
|
||||
border: 1px solid #dbeafe;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
padding: 10px 14px;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.file-preview small {
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
font-size: 0.85rem;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
#cadAreaTable {
|
||||
table-layout: fixed;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
#cadAreaTable th,
|
||||
#cadAreaTable td {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#cadAreaTable th:nth-child(1),
|
||||
#cadAreaTable td:nth-child(1) {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
#cadAreaTable th:nth-child(2),
|
||||
#cadAreaTable td:nth-child(2) {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#cadAreaTable th:nth-child(3),
|
||||
#cadAreaTable td:nth-child(3) {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
#cadAreaTable th:nth-child(4),
|
||||
#cadAreaTable td:nth-child(4),
|
||||
#cadAreaTable th:nth-child(5),
|
||||
#cadAreaTable td:nth-child(5),
|
||||
#cadAreaTable th:nth-child(6),
|
||||
#cadAreaTable td:nth-child(6) {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
#cadAreaTable th:nth-child(7),
|
||||
#cadAreaTable td:nth-child(7) {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#cadAreaTable th:nth-child(8),
|
||||
#cadAreaTable td:nth-child(8) {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.processing-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(15, 23, 42, 0.55);
|
||||
z-index: 9999;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.processing-box {
|
||||
background: white;
|
||||
border-radius: 18px;
|
||||
padding: 30px;
|
||||
min-width: 320px;
|
||||
text-align: center;
|
||||
box-shadow: 0 12px 35px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper" id="appWrapper">
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<?php include('include/topbar.php'); ?>
|
||||
|
||||
<div class="page-wrapper">
|
||||
<div class="page-content">
|
||||
|
||||
<div class="card p-3 mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5 class="mb-0">Calcolo Area da PDF CAD</h5>
|
||||
<small class="text-muted">Upload PDF vettoriali e calcolo automatico della superficie del profilo</small>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn back-dashboard" onclick="location.href='production_dashboard.php'">
|
||||
↩️ Torna alla Dashboard
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div id="dropZone" class="drop-zone">
|
||||
<div class="drop-zone-icon">📐</div>
|
||||
<h5 class="mb-1">Trascina qui uno o più PDF CAD</h5>
|
||||
<p class="text-muted mb-2">Oppure clicca per selezionare i file</p>
|
||||
<small class="text-muted">Formati accettati: PDF - massimo 25 MB per file</small>
|
||||
<input type="file" id="pdfFiles" accept="application/pdf,.pdf" multiple hidden>
|
||||
</div>
|
||||
|
||||
<div id="selectedFiles" class="mt-3"></div>
|
||||
|
||||
<div class="text-end mt-3">
|
||||
<button id="uploadBtn" class="btn btn-add" disabled>
|
||||
⬆️ Carica PDF
|
||||
</button>
|
||||
|
||||
<button id="processUploadedBtn" class="btn btn-outline-primary ms-2" disabled>
|
||||
⚙️ Procedi al calcolo
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card p-3">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Risultati Elaborazione</h5>
|
||||
<button id="processSelectedBtn" class="btn btn-add">
|
||||
⚙️ Calcola selezionati
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table id="cadAreaTable" class="table table-striped align-middle text-center" style="width:100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<input type="checkbox" id="selectAll">
|
||||
</th>
|
||||
<th>Nome PDF</th>
|
||||
<th>Stato</th>
|
||||
<th>Area mm²</th>
|
||||
<th>Area cm²</th>
|
||||
<th>Scala</th>
|
||||
<th>Confidenza</th>
|
||||
<th>Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<?php foreach ($jobs as $job): ?>
|
||||
<?php
|
||||
$status = $job['status'];
|
||||
|
||||
if ($status === 'completed') {
|
||||
$badge = "<span class='badge bg-success status-badge'>Completato</span>";
|
||||
} elseif ($status === 'processing') {
|
||||
$badge = "<span class='badge bg-warning text-dark status-badge'>In lavorazione</span>";
|
||||
} elseif ($status === 'error') {
|
||||
$badge = "<span class='badge bg-danger status-badge'>Errore</span>";
|
||||
} else {
|
||||
$badge = "<span class='badge bg-secondary status-badge'>Caricato</span>";
|
||||
}
|
||||
|
||||
$areaMm2 = $job['area_mm2'] !== null ? number_format((float)$job['area_mm2'], 3, ',', '.') : '-';
|
||||
$areaCm2 = $job['area_cm2'] !== null ? number_format((float)$job['area_cm2'], 4, ',', '.') : '-';
|
||||
$scale = $job['scale_detected'] ?: '-';
|
||||
$confidence = $job['confidence'] ?: '-';
|
||||
$fileUrl = htmlspecialchars($job['file_url'] ?? '', ENT_QUOTES, 'UTF-8');
|
||||
?>
|
||||
<tr data-id="<?= (int)$job['id']; ?>">
|
||||
<td>
|
||||
<?php if (in_array($job['status'], ['uploaded', 'error'], true)): ?>
|
||||
<input type="checkbox" class="row-check" value="<?= (int)$job['id']; ?>">
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
<td title="<?= htmlspecialchars($job['original_filename'], ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<?= htmlspecialchars($job['original_filename'], ENT_QUOTES, 'UTF-8'); ?>
|
||||
</td>
|
||||
|
||||
<td><?= $badge; ?></td>
|
||||
<td class="fw-semibold"><?= $areaMm2; ?></td>
|
||||
<td class="fw-semibold"><?= $areaCm2; ?></td>
|
||||
<td><?= htmlspecialchars($scale, ENT_QUOTES, 'UTF-8'); ?></td>
|
||||
<td><?= htmlspecialchars($confidence, ENT_QUOTES, 'UTF-8'); ?></td>
|
||||
|
||||
<td>
|
||||
<?php if (!empty($job['file_url'])): ?>
|
||||
<a href="<?= $fileUrl; ?>" target="_blank" class="btn btn-sm btn-outline-dark">
|
||||
📄 Apri PDF
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (in_array($job['status'], ['uploaded', 'error'], true)): ?>
|
||||
<button class="btn btn-sm btn-outline-primary process-one" data-id="<?= (int)$job['id']; ?>">
|
||||
⚙️ Calcola
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<button class="btn btn-sm btn-outline-danger delete-one" data-id="<?= (int)$job['id']; ?>">
|
||||
🗑️
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include('include/footer.php'); ?>
|
||||
</div>
|
||||
|
||||
<div class="processing-overlay" id="processingOverlay">
|
||||
<div class="processing-box">
|
||||
<div class="spinner-border text-primary mb-3" role="status"></div>
|
||||
<h5 class="mb-1">Elaborazione in corso</h5>
|
||||
<p class="text-muted mb-0">Invio dei PDF al servizio di calcolo area...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include('jsinclude.php'); ?>
|
||||
|
||||
<script>
|
||||
let selectedFiles = [];
|
||||
let uploadedIds = [];
|
||||
|
||||
const dropZone = document.getElementById('dropZone');
|
||||
const fileInput = document.getElementById('pdfFiles');
|
||||
const selectedFilesBox = document.getElementById('selectedFiles');
|
||||
const uploadBtn = document.getElementById('uploadBtn');
|
||||
const processUploadedBtn = document.getElementById('processUploadedBtn');
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#cadAreaTable').DataTable({
|
||||
order: [
|
||||
[1, 'asc']
|
||||
],
|
||||
pageLength: 25,
|
||||
language: {
|
||||
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
dropZone.addEventListener('click', function() {
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
dropZone.addEventListener('dragover', function(e) {
|
||||
e.preventDefault();
|
||||
dropZone.classList.add('dragover');
|
||||
});
|
||||
|
||||
dropZone.addEventListener('dragleave', function() {
|
||||
dropZone.classList.remove('dragover');
|
||||
});
|
||||
|
||||
dropZone.addEventListener('drop', function(e) {
|
||||
e.preventDefault();
|
||||
dropZone.classList.remove('dragover');
|
||||
handleFiles(e.dataTransfer.files);
|
||||
});
|
||||
|
||||
fileInput.addEventListener('change', function(e) {
|
||||
handleFiles(e.target.files);
|
||||
});
|
||||
|
||||
function handleFiles(files) {
|
||||
const incomingFiles = Array.from(files);
|
||||
|
||||
incomingFiles.forEach(file => {
|
||||
const isPdf = file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf');
|
||||
|
||||
if (!isPdf) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'File non valido',
|
||||
text: `${file.name} non è un PDF.`
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.size > 25 * 1024 * 1024) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'File troppo grande',
|
||||
text: `${file.name} supera il limite di 25 MB.`
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
selectedFiles.push(file);
|
||||
});
|
||||
|
||||
renderSelectedFiles();
|
||||
}
|
||||
|
||||
function renderSelectedFiles() {
|
||||
selectedFilesBox.innerHTML = '';
|
||||
|
||||
selectedFiles.forEach((file, index) => {
|
||||
const sizeMb = (file.size / 1024 / 1024).toFixed(2);
|
||||
|
||||
const item = document.createElement('div');
|
||||
item.className = 'file-preview';
|
||||
item.innerHTML = `
|
||||
<div>
|
||||
<strong>📄 ${escapeHtml(file.name)}</strong><br>
|
||||
<small>${sizeMb} MB</small>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="removeSelectedFile(${index})">
|
||||
Rimuovi
|
||||
</button>
|
||||
`;
|
||||
selectedFilesBox.appendChild(item);
|
||||
});
|
||||
|
||||
uploadBtn.disabled = selectedFiles.length === 0;
|
||||
}
|
||||
|
||||
function removeSelectedFile(index) {
|
||||
selectedFiles.splice(index, 1);
|
||||
renderSelectedFiles();
|
||||
}
|
||||
|
||||
uploadBtn.addEventListener('click', function() {
|
||||
if (selectedFiles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
selectedFiles.forEach(file => {
|
||||
formData.append('pdf_files[]', file);
|
||||
});
|
||||
|
||||
showOverlay();
|
||||
|
||||
fetch('cad_area_upload.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
hideOverlay();
|
||||
|
||||
if (!data.success) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Errore upload',
|
||||
text: data.message || 'Upload non riuscito'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
uploadedIds = data.ids || [];
|
||||
selectedFiles = [];
|
||||
renderSelectedFiles();
|
||||
|
||||
processUploadedBtn.disabled = uploadedIds.length === 0;
|
||||
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'PDF caricati',
|
||||
text: 'Ora puoi procedere al calcolo area.',
|
||||
confirmButtonText: 'OK'
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
hideOverlay();
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Errore',
|
||||
text: error.message || 'Errore durante upload'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
processUploadedBtn.addEventListener('click', function() {
|
||||
if (uploadedIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
processJobs(uploadedIds);
|
||||
});
|
||||
|
||||
$('#selectAll').on('change', function() {
|
||||
$('.row-check').prop('checked', this.checked);
|
||||
});
|
||||
|
||||
$('#processSelectedBtn').on('click', function() {
|
||||
const ids = $('.row-check:checked').map(function() {
|
||||
return parseInt($(this).val(), 10);
|
||||
}).get();
|
||||
|
||||
if (ids.length === 0) {
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Nessun PDF selezionato',
|
||||
text: 'Seleziona almeno un PDF da elaborare.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
processJobs(ids);
|
||||
});
|
||||
|
||||
$(document).on('click', '.process-one', function() {
|
||||
const id = parseInt($(this).data('id'), 10);
|
||||
processJobs([id]);
|
||||
});
|
||||
|
||||
$(document).on('click', '.delete-one', function() {
|
||||
const id = parseInt($(this).data('id'), 10);
|
||||
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Eliminare il PDF?',
|
||||
text: 'Il file e il relativo risultato saranno rimossi.',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Sì, elimina',
|
||||
cancelButtonText: 'Annulla'
|
||||
}).then(result => {
|
||||
if (!result.isConfirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('cad_area_delete.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: id
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Errore',
|
||||
text: data.message || 'Eliminazione non riuscita'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function processJobs(ids) {
|
||||
showOverlay();
|
||||
|
||||
fetch('cad_area_process.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
ids: ids
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
hideOverlay();
|
||||
|
||||
if (!data.success) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Errore elaborazione',
|
||||
text: data.message || 'Elaborazione non riuscita'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Elaborazione completata',
|
||||
text: 'I risultati sono stati aggiornati.',
|
||||
confirmButtonText: 'OK'
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
hideOverlay();
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Errore',
|
||||
text: error.message || 'Errore durante elaborazione'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showOverlay() {
|
||||
$('#processingOverlay').css('display', 'flex');
|
||||
}
|
||||
|
||||
function hideOverlay() {
|
||||
$('#processingOverlay').hide();
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
return text.replace(/[&<>"']/g, function(match) {
|
||||
return ({
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
})[match];
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once(__DIR__ . '/include/headscript.php');
|
||||
|
||||
try {
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$iduser = $iduserlogin ?? null;
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($input['id'] ?? 0);
|
||||
|
||||
if ($id <= 0) {
|
||||
throw new Exception('ID non valido.');
|
||||
}
|
||||
|
||||
if ($iduser === null) {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT *
|
||||
FROM cad_area_jobs
|
||||
WHERE id = :id
|
||||
LIMIT 1
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
':id' => $id
|
||||
]);
|
||||
} else {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT *
|
||||
FROM cad_area_jobs
|
||||
WHERE id = :id
|
||||
AND iduser = :iduser
|
||||
LIMIT 1
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
':id' => $id,
|
||||
':iduser' => $iduser
|
||||
]);
|
||||
}
|
||||
|
||||
$job = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$job) {
|
||||
throw new Exception('Record non trovato.');
|
||||
}
|
||||
|
||||
if (!empty($job['file_path']) && file_exists($job['file_path'])) {
|
||||
unlink($job['file_path']);
|
||||
}
|
||||
|
||||
$stmtDelete = $pdo->prepare("
|
||||
DELETE FROM cad_area_jobs
|
||||
WHERE id = :id
|
||||
");
|
||||
$stmtDelete->execute([':id' => $id]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
error_log('CAD area delete error: ' . $e->getMessage());
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once(__DIR__ . '/include/headscript.php');
|
||||
|
||||
try {
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$iduser = $iduserlogin ?? null;
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (empty($input['ids']) || !is_array($input['ids'])) {
|
||||
throw new Exception('Nessun file selezionato.');
|
||||
}
|
||||
|
||||
$ids = array_values(array_filter(array_map('intval', $input['ids'])));
|
||||
|
||||
if (empty($ids)) {
|
||||
throw new Exception('ID non validi.');
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Python service URL.
|
||||
* In produzione su cPanel/Keliweb lo sostituiremo con l'URL reale del servizio Python.
|
||||
*/
|
||||
$pythonServiceUrl = 'http://127.0.0.1:5055/calculate';
|
||||
|
||||
$results = [];
|
||||
|
||||
foreach ($ids as $id) {
|
||||
|
||||
/*
|
||||
* Recupero job.
|
||||
* Uso placeholder ? per evitare errori PDO HY093.
|
||||
*/
|
||||
if ($iduser === null || $iduser === '') {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT *
|
||||
FROM cad_area_jobs
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
$id
|
||||
]);
|
||||
} else {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT *
|
||||
FROM cad_area_jobs
|
||||
WHERE id = ?
|
||||
AND iduser = ?
|
||||
LIMIT 1
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
$id,
|
||||
$iduser
|
||||
]);
|
||||
}
|
||||
|
||||
$job = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$job) {
|
||||
$results[] = [
|
||||
'id' => $id,
|
||||
'success' => false,
|
||||
'message' => 'Record non trovato.'
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($job['file_path']) || !file_exists($job['file_path'])) {
|
||||
updateJobError($pdo, $id, 'File PDF non trovato sul server.');
|
||||
|
||||
$results[] = [
|
||||
'id' => $id,
|
||||
'success' => false,
|
||||
'message' => 'File PDF non trovato sul server.'
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
$stmtProcessing = $pdo->prepare("
|
||||
UPDATE cad_area_jobs
|
||||
SET
|
||||
status = ?,
|
||||
message = ?
|
||||
WHERE id = ?
|
||||
");
|
||||
|
||||
$stmtProcessing->execute([
|
||||
'processing',
|
||||
null,
|
||||
$id
|
||||
]);
|
||||
|
||||
$pythonResponse = callPythonAreaService(
|
||||
$pythonServiceUrl,
|
||||
$job['file_path'],
|
||||
$job['original_filename']
|
||||
);
|
||||
|
||||
if (empty($pythonResponse['success'])) {
|
||||
updateJobError(
|
||||
$pdo,
|
||||
$id,
|
||||
$pythonResponse['message'] ?? 'Errore servizio Python.',
|
||||
$pythonResponse
|
||||
);
|
||||
|
||||
$results[] = [
|
||||
'id' => $id,
|
||||
'success' => false,
|
||||
'message' => $pythonResponse['message'] ?? 'Errore servizio Python.',
|
||||
'python_response' => $pythonResponse
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
$areaMm2 = isset($pythonResponse['area_mm2']) && $pythonResponse['area_mm2'] !== null
|
||||
? (float)$pythonResponse['area_mm2']
|
||||
: null;
|
||||
|
||||
$areaCm2 = isset($pythonResponse['area_cm2']) && $pythonResponse['area_cm2'] !== null
|
||||
? (float)$pythonResponse['area_cm2']
|
||||
: ($areaMm2 !== null ? $areaMm2 / 100 : null);
|
||||
|
||||
$areaM2 = isset($pythonResponse['area_m2']) && $pythonResponse['area_m2'] !== null
|
||||
? (float)$pythonResponse['area_m2']
|
||||
: ($areaMm2 !== null ? $areaMm2 / 1000000 : null);
|
||||
|
||||
$stmtUpdate = $pdo->prepare("
|
||||
UPDATE cad_area_jobs
|
||||
SET
|
||||
status = ?,
|
||||
area_mm2 = ?,
|
||||
area_cm2 = ?,
|
||||
area_m2 = ?,
|
||||
scale_detected = ?,
|
||||
confidence = ?,
|
||||
message = ?,
|
||||
python_response = ?
|
||||
WHERE id = ?
|
||||
");
|
||||
|
||||
$stmtUpdate->execute([
|
||||
'completed',
|
||||
$areaMm2,
|
||||
$areaCm2,
|
||||
$areaM2,
|
||||
$pythonResponse['scale_detected'] ?? null,
|
||||
$pythonResponse['confidence'] ?? null,
|
||||
$pythonResponse['message'] ?? null,
|
||||
json_encode($pythonResponse, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
|
||||
$id
|
||||
]);
|
||||
|
||||
$results[] = [
|
||||
'id' => $id,
|
||||
'success' => true,
|
||||
'area_mm2' => $areaMm2,
|
||||
'area_cm2' => $areaCm2,
|
||||
'area_m2' => $areaM2,
|
||||
'message' => $pythonResponse['message'] ?? null
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'results' => $results
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
error_log('CAD area process error: ' . $e->getMessage());
|
||||
error_log('File: ' . $e->getFile());
|
||||
error_log('Line: ' . $e->getLine());
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine()
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
function callPythonAreaService(string $url, string $filePath, string $originalFilename): array
|
||||
{
|
||||
if (!class_exists('CURLFile')) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'CURLFile non disponibile sul server PHP.'
|
||||
];
|
||||
}
|
||||
|
||||
if (!function_exists('curl_init')) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Estensione PHP cURL non disponibile.'
|
||||
];
|
||||
}
|
||||
|
||||
$curlFile = new CURLFile($filePath, 'application/pdf', $originalFilename);
|
||||
|
||||
$postFields = [
|
||||
'file' => $curlFile,
|
||||
'mode' => 'pdf_vector',
|
||||
'scale_ratio' => '1'
|
||||
];
|
||||
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $postFields,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 180,
|
||||
CURLOPT_CONNECTTIMEOUT => 20,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSL_VERIFYHOST => false
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if ($response === false) {
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Errore cURL verso Python: ' . $error
|
||||
];
|
||||
}
|
||||
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
$decoded = json_decode($response, true);
|
||||
|
||||
if ($httpCode < 200 || $httpCode >= 300) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Servizio Python HTTP ' . $httpCode,
|
||||
'raw_response' => $response
|
||||
];
|
||||
}
|
||||
|
||||
if (!is_array($decoded)) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Risposta Python non valida.',
|
||||
'raw_response' => $response
|
||||
];
|
||||
}
|
||||
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
|
||||
function updateJobError(PDO $pdo, int $id, string $message, ?array $pythonResponse = null): void
|
||||
{
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE cad_area_jobs
|
||||
SET
|
||||
status = ?,
|
||||
message = ?,
|
||||
python_response = ?
|
||||
WHERE id = ?
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
'error',
|
||||
$message,
|
||||
$pythonResponse
|
||||
? json_encode($pythonResponse, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||
: null,
|
||||
$id
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once(__DIR__ . '/include/headscript.php');
|
||||
|
||||
try {
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$iduser = $iduserlogin ?? null;
|
||||
|
||||
$uploadDir = __DIR__ . '/uploads/cad_area/originals/';
|
||||
$publicBaseUrl = 'uploads/cad_area/originals/';
|
||||
|
||||
if (!is_dir($uploadDir)) {
|
||||
mkdir($uploadDir, 0755, true);
|
||||
}
|
||||
|
||||
if (empty($_FILES['pdf_files'])) {
|
||||
throw new Exception('Nessun file ricevuto.');
|
||||
}
|
||||
|
||||
$files = $_FILES['pdf_files'];
|
||||
$insertedIds = [];
|
||||
|
||||
for ($i = 0; $i < count($files['name']); $i++) {
|
||||
if ($files['error'][$i] !== UPLOAD_ERR_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$originalName = $files['name'][$i];
|
||||
$tmpName = $files['tmp_name'][$i];
|
||||
$size = (int)$files['size'][$i];
|
||||
|
||||
$extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
|
||||
|
||||
if ($extension !== 'pdf') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($size > 25 * 1024 * 1024) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$safeBaseName = preg_replace('/[^a-zA-Z0-9_\-]/', '_', pathinfo($originalName, PATHINFO_FILENAME));
|
||||
$storedName = date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '_' . $safeBaseName . '.pdf';
|
||||
|
||||
$targetPath = $uploadDir . $storedName;
|
||||
|
||||
if (!move_uploaded_file($tmpName, $targetPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$relativeUrl = $publicBaseUrl . $storedName;
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO cad_area_jobs
|
||||
(
|
||||
iduser,
|
||||
original_filename,
|
||||
stored_filename,
|
||||
file_path,
|
||||
file_url,
|
||||
file_size,
|
||||
status
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
:iduser,
|
||||
:original_filename,
|
||||
:stored_filename,
|
||||
:file_path,
|
||||
:file_url,
|
||||
:file_size,
|
||||
'uploaded'
|
||||
)
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
':iduser' => $iduser,
|
||||
':original_filename' => $originalName,
|
||||
':stored_filename' => $storedName,
|
||||
':file_path' => $targetPath,
|
||||
':file_url' => $relativeUrl,
|
||||
':file_size' => $size
|
||||
]);
|
||||
|
||||
$insertedIds[] = (int)$pdo->lastInsertId();
|
||||
}
|
||||
|
||||
if (empty($insertedIds)) {
|
||||
throw new Exception('Nessun PDF valido caricato.');
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'ids' => $insertedIds
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
error_log('CAD area upload error: ' . $e->getMessage());
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user