Compare commits
2 Commits
fe604791f0
...
141dd68716
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
141dd68716 | ||
|
|
79a9217307 |
@@ -49,65 +49,76 @@
|
|||||||
</aside>
|
</aside>
|
||||||
<!-- Main content -->
|
<!-- Main content -->
|
||||||
<main class="col-md-10 ms-sm-auto col-lg-11 offset-md-2 offset-lg-1 px-md-4">
|
<main class="col-md-10 ms-sm-auto col-lg-11 offset-md-2 offset-lg-1 px-md-4">
|
||||||
<h1 class="mt-4">Base de données</h1>
|
<h1 class="mt-4" data-i18n="database.title">Base de données</h1>
|
||||||
<p>Le capteur enregistre en local les données de mesures. Vous pouvez ici les consulter et les télécharger.</p>
|
<p data-i18n="database.description">Le capteur enregistre en local les données de mesures. Vous pouvez ici les consulter et les télécharger.</p>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
|
|
||||||
<div class="col-sm-5">
|
<div class="col-sm-5">
|
||||||
<div class="card text-dark bg-light">
|
<div class="card text-dark bg-light">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Consulter la base de donnée</h5>
|
<h5 class="card-title" data-i18n="database.viewDatabase">Consulter la base de donnée</h5>
|
||||||
<!-- Dropdown to select number of records -->
|
<!-- Dropdown to select number of records -->
|
||||||
<div class="d-flex align-items-center mb-3">
|
<div class="d-flex align-items-center mb-3">
|
||||||
<label for="records_limit" class="form-label me-2">Nombre de mesures:</label>
|
<label for="records_limit" class="form-label me-2" data-i18n="database.numberOfMeasures">Nombre de mesures:</label>
|
||||||
<select id="records_limit" class="form-select w-auto">
|
<select id="records_limit" class="form-select w-auto">
|
||||||
<option value="10" selected>10 dernières</option>
|
<option value="10" selected data-i18n="database.last10">10 dernières</option>
|
||||||
<option value="20">20 dernières</option>
|
<option value="20" data-i18n="database.last20">20 dernières</option>
|
||||||
<option value="30">30 dernières</option>
|
<option value="30" data-i18n="database.last30">30 dernières</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM',getSelectedLimit(),false)">Mesures PM</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM',getSelectedLimit(),false)" data-i18n="database.pmMeasures">Mesures PM</button>
|
||||||
<button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',getSelectedLimit(),false)">Mesures Temp/Hum</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',getSelectedLimit(),false)" data-i18n="database.tempHumMeasures">Mesures Temp/Hum</button>
|
||||||
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM_5channels',getSelectedLimit(),false)">Mesures PM (5 canaux)</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM_5channels',getSelectedLimit(),false)" data-i18n="database.pm5Channels">Mesures PM (5 canaux)</button>
|
||||||
<button class="btn btn-primary" onclick="get_data_sqlite('data_envea',getSelectedLimit(),false)">Sonde Cairsens</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_envea',getSelectedLimit(),false)" data-i18n="database.cairsensProbe">Sonde Cairsens</button>
|
||||||
<button class="btn btn-primary" onclick="get_data_sqlite('data_NOISE',getSelectedLimit(),false)">Sonde bruit</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_NOISE',getSelectedLimit(),false)" data-i18n="database.noiseProbe">Sonde bruit</button>
|
||||||
|
|
||||||
<button class="btn btn-primary" onclick="get_data_sqlite('data_WIND',getSelectedLimit(),false)">Sonde Vent</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_WIND',getSelectedLimit(),false)" data-i18n="database.windProbe">Sonde Vent</button>
|
||||||
<button class="btn btn-primary" onclick="get_data_sqlite('data_MPPT',getSelectedLimit(),false)">Batterie</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_MPPT',getSelectedLimit(),false)" data-i18n="database.battery">Batterie</button>
|
||||||
|
|
||||||
<button class="btn btn-warning" onclick="get_data_sqlite('timestamp_table',getSelectedLimit(),false)">Timestamp Table</button>
|
<button class="btn btn-warning" onclick="get_data_sqlite('timestamp_table',getSelectedLimit(),false)" data-i18n="database.timestampTable">Timestamp Table</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-5">
|
<div class="col-sm-5">
|
||||||
<div class="card text-dark bg-light">
|
<div class="card text-dark bg-light">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Télécharger les données</h5>
|
<h5 class="card-title" data-i18n="database.downloadData">Télécharger les données</h5>
|
||||||
<!-- Date selection for download -->
|
<!-- Date selection for download -->
|
||||||
<div class="d-flex align-items-center gap-3 mb-3">
|
<div class="d-flex align-items-center gap-3 mb-3">
|
||||||
<label for="start_date" class="form-label">Date de début:</label>
|
<label for="start_date" class="form-label" data-i18n="database.startDate">Date de début:</label>
|
||||||
<input type="date" id="start_date" class="form-control w-auto">
|
<input type="date" id="start_date" class="form-control w-auto">
|
||||||
<label for="end_date" class="form-label">Date de fin:</label>
|
<label for="end_date" class="form-label" data-i18n="database.endDate">Date de fin:</label>
|
||||||
<input type="date" id="end_date" class="form-control w-auto">
|
<input type="date" id="end_date" class="form-control w-auto">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM',10,true, getStartDate(), getEndDate())">Mesures PM</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM',10,true, getStartDate(), getEndDate())" data-i18n="database.pmMeasures">Mesures PM</button>
|
||||||
<button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',10,true, getStartDate(), getEndDate())">Mesures Temp/Hum</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',10,true, getStartDate(), getEndDate())" data-i18n="database.tempHumMeasures">Mesures Temp/Hum</button>
|
||||||
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM_5channels',10,true, getStartDate(), getEndDate())">Mesures PM (5 canaux)</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM_5channels',10,true, getStartDate(), getEndDate())" data-i18n="database.pm5Channels">Mesures PM (5 canaux)</button>
|
||||||
<button class="btn btn-primary" onclick="get_data_sqlite('data_envea',10,true, getStartDate(), getEndDate())">Sonde Cairsens</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_envea',10,true, getStartDate(), getEndDate())" data-i18n="database.cairsensProbe">Sonde Cairsens</button>
|
||||||
<button class="btn btn-primary" onclick="get_data_sqlite('data_NOISE',10,true, getStartDate(), getEndDate())">Sonde Bruit</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_NOISE',10,true, getStartDate(), getEndDate())" data-i18n="database.noiseProbe">Sonde Bruit</button>
|
||||||
|
|
||||||
<button class="btn btn-primary" onclick="get_data_sqlite('data_mppt',10,true, getStartDate(), getEndDate())">Batterie</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_mppt',10,true, getStartDate(), getEndDate())" data-i18n="database.battery">Batterie</button>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-5">
|
||||||
|
<div class="card text-white bg-danger">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title" data-i18n="database.dangerZone">Zone dangereuse</h5>
|
||||||
|
<p class="card-text" data-i18n="database.dangerWarning">Attention: Cette action est irréversible!</p>
|
||||||
|
<button class="btn btn-dark" onclick="emptySensorTables()" data-i18n="database.emptyAllTables">Vider toutes les tables de capteurs</button>
|
||||||
|
<small class="d-block mt-2" data-i18n="database.emptyTablesNote">Note: Les tables de configuration et horodatage seront préservées.</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<div class="row mt-2">
|
<div class="row mt-2">
|
||||||
@@ -441,6 +452,74 @@ function downloadCSV(response, table) {
|
|||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to empty all sensor tables
|
||||||
|
function emptySensorTables() {
|
||||||
|
// Show confirmation dialog
|
||||||
|
const confirmed = confirm(
|
||||||
|
"WARNING: This will permanently delete ALL sensor data from the database!\n\n" +
|
||||||
|
"The following tables will be emptied:\n" +
|
||||||
|
"- data_NPM\n" +
|
||||||
|
"- data_NPM_5channels\n" +
|
||||||
|
"- data_BME280\n" +
|
||||||
|
"- data_envea\n" +
|
||||||
|
"- data_WIND\n" +
|
||||||
|
"- data_MPPT\n" +
|
||||||
|
"- data_NOISE\n\n" +
|
||||||
|
"Configuration and timestamp tables will be preserved.\n\n" +
|
||||||
|
"Are you absolutely sure you want to continue?"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
console.log("Empty sensor tables operation cancelled by user");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show loading message
|
||||||
|
const tableDataDiv = document.getElementById("table_data");
|
||||||
|
tableDataDiv.innerHTML = '<div class="alert alert-info">Emptying sensor tables... Please wait...</div>';
|
||||||
|
|
||||||
|
// Make AJAX request to empty tables
|
||||||
|
$.ajax({
|
||||||
|
url: 'launcher.php?type=empty_sensor_tables',
|
||||||
|
dataType: 'json',
|
||||||
|
method: 'GET',
|
||||||
|
success: function(response) {
|
||||||
|
console.log("Empty sensor tables response:", response);
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
// Show success message
|
||||||
|
let message = '<div class="alert alert-success">';
|
||||||
|
message += '<h5>Success!</h5>';
|
||||||
|
message += '<p>' + response.message + '</p>';
|
||||||
|
|
||||||
|
if (response.tables_processed && response.tables_processed.length > 0) {
|
||||||
|
message += '<p><strong>Tables emptied:</strong></p><ul>';
|
||||||
|
response.tables_processed.forEach(table => {
|
||||||
|
message += `<li>${table.name}: ${table.deleted} records deleted</li>`;
|
||||||
|
});
|
||||||
|
message += '</ul>';
|
||||||
|
}
|
||||||
|
|
||||||
|
message += '</div>';
|
||||||
|
tableDataDiv.innerHTML = message;
|
||||||
|
} else {
|
||||||
|
// Show error message
|
||||||
|
tableDataDiv.innerHTML = `<div class="alert alert-danger">
|
||||||
|
<h5>Error!</h5>
|
||||||
|
<p>${response.message || response.error || 'Unknown error occurred'}</p>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
console.error('AJAX request failed:', status, error);
|
||||||
|
tableDataDiv.innerHTML = `<div class="alert alert-danger">
|
||||||
|
<h5>Error!</h5>
|
||||||
|
<p>Failed to empty sensor tables: ${error}</p>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -51,16 +51,16 @@
|
|||||||
</aside>
|
</aside>
|
||||||
<!-- Main content -->
|
<!-- Main content -->
|
||||||
<main class="col-md-9 ms-sm-auto col-lg-10 offset-md-3 offset-lg-2 px-md-4">
|
<main class="col-md-9 ms-sm-auto col-lg-10 offset-md-3 offset-lg-2 px-md-4">
|
||||||
<h1 class="mt-4">Votre capteur</h1>
|
<h1 class="mt-4" data-i18n="home.title">Votre capteur</h1>
|
||||||
<p>Bienvenue sur votre interface de configuration de votre capteur.</p>
|
<p data-i18n="home.welcome">Bienvenue sur votre interface de configuration de votre capteur.</p>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
|
|
||||||
<!-- Card NPM values -->
|
<!-- Card NPM values -->
|
||||||
<div class="col-sm-4 mt-2">
|
<div class="col-sm-4 mt-2">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Mesures PM</h5>
|
<h5 class="card-title" data-i18n="home.pmMeasures">Mesures PM</h5>
|
||||||
<canvas id="sensorPMChart" style="width: 100%; max-width: 600px; height: 200px;"></canvas>
|
<canvas id="sensorPMChart" style="width: 100%; max-width: 600px; height: 200px;"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,14 +68,14 @@
|
|||||||
|
|
||||||
<!-- Card Linux Stats -->
|
<!-- Card Linux Stats -->
|
||||||
<div class="col-sm-4 mt-2">
|
<div class="col-sm-4 mt-2">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Linux stats</h5>
|
<h5 class="card-title" data-i18n="home.linuxStats">Statistiques Linux</h5>
|
||||||
<p class="card-text">Disk usage (total size <span id="disk_size"></span> Gb) </p>
|
<p class="card-text"><span data-i18n="home.diskUsage">Utilisation du disque (taille totale</span> <span id="disk_size"></span> Gb) </p>
|
||||||
<div id="disk_space"></div>
|
<div id="disk_space"></div>
|
||||||
<p class="card-text">Memory usage (total size <span id="memory_size"></span> Mb) </p>
|
<p class="card-text"><span data-i18n="home.memoryUsage">Utilisation de la mémoire (taille totale</span> <span id="memory_size"></span> Mb) </p>
|
||||||
<div id="memory_space"></div>
|
<div id="memory_space"></div>
|
||||||
<p class="card-text"> Database size: <span id="database_size"></span> </p>
|
<p class="card-text"><span data-i18n="home.databaseSize">Taille de la base de données:</span> <span id="database_size"></span> </p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -60,5 +60,46 @@
|
|||||||
"map": "Map",
|
"map": "Map",
|
||||||
"terminal": "Terminal",
|
"terminal": "Terminal",
|
||||||
"admin": "Admin"
|
"admin": "Admin"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"title": "Your Sensor",
|
||||||
|
"welcome": "Welcome to your sensor configuration interface.",
|
||||||
|
"pmMeasures": "PM Measurements",
|
||||||
|
"linuxStats": "Linux Statistics",
|
||||||
|
"diskUsage": "Disk usage (total size",
|
||||||
|
"memoryUsage": "Memory usage (total size",
|
||||||
|
"databaseSize": "Database size:"
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"title": "Database",
|
||||||
|
"description": "The sensor records measurement data locally. You can view and download it here.",
|
||||||
|
"viewDatabase": "View Database",
|
||||||
|
"numberOfMeasures": "Number of measurements:",
|
||||||
|
"last10": "Last 10",
|
||||||
|
"last20": "Last 20",
|
||||||
|
"last30": "Last 30",
|
||||||
|
"pmMeasures": "PM Measurements",
|
||||||
|
"tempHumMeasures": "Temp/Hum Measurements",
|
||||||
|
"pm5Channels": "PM Measurements (5 channels)",
|
||||||
|
"cairsensProbe": "Cairsens Probe",
|
||||||
|
"noiseProbe": "Noise Probe",
|
||||||
|
"windProbe": "Wind Probe",
|
||||||
|
"battery": "Battery",
|
||||||
|
"timestampTable": "Timestamp Table",
|
||||||
|
"downloadData": "Download Data",
|
||||||
|
"startDate": "Start date:",
|
||||||
|
"endDate": "End date:",
|
||||||
|
"dangerZone": "Danger Zone",
|
||||||
|
"dangerWarning": "Warning: This action is irreversible!",
|
||||||
|
"emptyAllTables": "Empty all sensor tables",
|
||||||
|
"emptyTablesNote": "Note: Configuration and timestamp tables will be preserved."
|
||||||
|
},
|
||||||
|
"logs": {
|
||||||
|
"title": "The Log",
|
||||||
|
"description": "The log allows you to know if the sensor processes are running correctly.",
|
||||||
|
"saraLogs": "Sara logs",
|
||||||
|
"bootLogs": "Boot logs",
|
||||||
|
"refresh": "Refresh",
|
||||||
|
"clear": "Clear"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,5 +60,46 @@
|
|||||||
"map": "Carte",
|
"map": "Carte",
|
||||||
"terminal": "Terminal",
|
"terminal": "Terminal",
|
||||||
"admin": "Admin"
|
"admin": "Admin"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"title": "Votre capteur",
|
||||||
|
"welcome": "Bienvenue sur votre interface de configuration de votre capteur.",
|
||||||
|
"pmMeasures": "Mesures PM",
|
||||||
|
"linuxStats": "Statistiques Linux",
|
||||||
|
"diskUsage": "Utilisation du disque (taille totale",
|
||||||
|
"memoryUsage": "Utilisation de la mémoire (taille totale",
|
||||||
|
"databaseSize": "Taille de la base de données:"
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"title": "Base de données",
|
||||||
|
"description": "Le capteur enregistre en local les données de mesures. Vous pouvez ici les consulter et les télécharger.",
|
||||||
|
"viewDatabase": "Consulter la base de donnée",
|
||||||
|
"numberOfMeasures": "Nombre de mesures:",
|
||||||
|
"last10": "10 dernières",
|
||||||
|
"last20": "20 dernières",
|
||||||
|
"last30": "30 dernières",
|
||||||
|
"pmMeasures": "Mesures PM",
|
||||||
|
"tempHumMeasures": "Mesures Temp/Hum",
|
||||||
|
"pm5Channels": "Mesures PM (5 canaux)",
|
||||||
|
"cairsensProbe": "Sonde Cairsens",
|
||||||
|
"noiseProbe": "Sonde bruit",
|
||||||
|
"windProbe": "Sonde Vent",
|
||||||
|
"battery": "Batterie",
|
||||||
|
"timestampTable": "Timestamp Table",
|
||||||
|
"downloadData": "Télécharger les données",
|
||||||
|
"startDate": "Date de début:",
|
||||||
|
"endDate": "Date de fin:",
|
||||||
|
"dangerZone": "Zone dangereuse",
|
||||||
|
"dangerWarning": "Attention: Cette action est irréversible!",
|
||||||
|
"emptyAllTables": "Vider toutes les tables de capteurs",
|
||||||
|
"emptyTablesNote": "Note: Les tables de configuration et horodatage seront préservées."
|
||||||
|
},
|
||||||
|
"logs": {
|
||||||
|
"title": "Le journal",
|
||||||
|
"description": "Le journal des logs permet de savoir si les processus du capteur se déroulent correctement.",
|
||||||
|
"saraLogs": "Sara logs",
|
||||||
|
"bootLogs": "Boot logs",
|
||||||
|
"refresh": "Refresh",
|
||||||
|
"clear": "Clear"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1270,13 +1270,68 @@ if ($type == "toggle_systemd_service") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Empty all sensor tables (preserve config and timestamp tables)
|
||||||
|
if ($type == "empty_sensor_tables") {
|
||||||
|
try {
|
||||||
|
// Execute the empty sensor tables script
|
||||||
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/empty_sensor_tables.py 2>&1';
|
||||||
|
$output = shell_exec($command);
|
||||||
|
|
||||||
|
// Try to extract JSON result from output
|
||||||
|
$json_start = strpos($output, '[JSON_RESULT]');
|
||||||
|
if ($json_start !== false) {
|
||||||
|
$json_data = substr($output, $json_start + strlen('[JSON_RESULT]'));
|
||||||
|
$json_data = trim($json_data);
|
||||||
|
|
||||||
|
// Find the first { and last }
|
||||||
|
$first_brace = strpos($json_data, '{');
|
||||||
|
$last_brace = strrpos($json_data, '}');
|
||||||
|
|
||||||
|
if ($first_brace !== false && $last_brace !== false) {
|
||||||
|
$json_data = substr($json_data, $first_brace, $last_brace - $first_brace + 1);
|
||||||
|
$result = json_decode($json_data, true);
|
||||||
|
|
||||||
|
if ($result !== null) {
|
||||||
|
echo json_encode($result);
|
||||||
|
} else {
|
||||||
|
// JSON decode failed, return raw output
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Tables emptied',
|
||||||
|
'output' => $output
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Tables emptied',
|
||||||
|
'output' => $output
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No JSON marker found, return raw output
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Tables emptied',
|
||||||
|
'output' => $output
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Script execution failed: ' . $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
_____ ____ _ _ _
|
_____ ____ _ _ _
|
||||||
| ____|_ ____ _____ __ _ | _ \ ___| |_ ___ ___| |_(_) ___ _ __
|
| ____|_ ____ _____ __ _ | _ \ ___| |_ ___ ___| |_(_) ___ _ __
|
||||||
| _| | '_ \ \ / / _ \/ _` | | | | |/ _ \ __/ _ \/ __| __| |/ _ \| '_ \
|
| _| | '_ \ \ / / _ \/ _` | | | | |/ _ \ __/ _ \/ __| __| |/ _ \| '_ \
|
||||||
| |___| | | \ V / __/ (_| | | |_| | __/ || __/ (__| |_| | (_) | | | |
|
| |___| | | \ V / __/ (_| | | |_| | __/ || __/ (__| |_| | (_) | | | |
|
||||||
|_____|_| |_|\_/ \___|\__,_| |____/ \___|\__\___|\___|\__|_|\___/|_| |_|
|
|_____|_| |_|\_/ \___|\__,_| |____/ \___|\__\___|\___|\__|_|\___/|_| |_|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Detect Envea devices on specified port
|
// Detect Envea devices on specified port
|
||||||
|
|||||||
@@ -49,30 +49,30 @@
|
|||||||
</aside>
|
</aside>
|
||||||
<!-- Main content -->
|
<!-- Main content -->
|
||||||
<main class="col-md-10 ms-sm-auto col-lg-11 offset-md-2 offset-lg-1 px-md-4">
|
<main class="col-md-10 ms-sm-auto col-lg-11 offset-md-2 offset-lg-1 px-md-4">
|
||||||
<h1 class="mt-4">Le journal</h1>
|
<h1 class="mt-4" data-i18n="logs.title">Le journal</h1>
|
||||||
<p>Le journal des logs permet de savoir si les processus du capteur se déroulent correctement.</p>
|
<p data-i18n="logs.description">Le journal des logs permet de savoir si les processus du capteur se déroulent correctement.</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- card 1 -->
|
<!-- card 1 -->
|
||||||
<div class="col-lg-6 col-12">
|
<div class="col-lg-6 col-12">
|
||||||
<div class="card" style="height: 80vh;">
|
<div class="card" style="height: 80vh;">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
Sara logs
|
<span data-i18n="logs.saraLogs">Sara logs</span>
|
||||||
<button type="submit" class="btn btn-secondary btn-sm" id="refresh-master-log">Refresh</button>
|
<button type="submit" class="btn btn-secondary btn-sm" id="refresh-master-log" data-i18n="logs.refresh">Refresh</button>
|
||||||
<button type="submit" class="btn btn-secondary btn-sm" onclick="clear_loopLogs()">Clear</button>
|
<button type="submit" class="btn btn-secondary btn-sm" onclick="clear_loopLogs()" data-i18n="logs.clear">Clear</button>
|
||||||
|
|
||||||
<span id="script_running"></span>
|
<span id="script_running"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body overflow-auto" id="card_loop_content">
|
<div class="card-body overflow-auto" id="card_loop_content">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- card 2 -->
|
<!-- card 2 -->
|
||||||
<div class="col-lg-6 col-12">
|
<div class="col-lg-6 col-12">
|
||||||
<div class="card" style="height: 80vh;">
|
<div class="card" style="height: 80vh;">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
Boot logs
|
<span data-i18n="logs.bootLogs">Boot logs</span>
|
||||||
<button type="submit" class="btn btn-secondary btn-sm" id="refresh-boot-log">Refresh</button>
|
<button type="submit" class="btn btn-secondary btn-sm" id="refresh-boot-log" data-i18n="logs.refresh">Refresh</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body overflow-auto" id="card_boot_content">
|
<div class="card-body overflow-auto" id="card_boot_content">
|
||||||
|
|
||||||
|
|||||||
204
sqlite/empty_sensor_tables.py
Normal file
204
sqlite/empty_sensor_tables.py
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
'''
|
||||||
|
____ ___ _ _ _
|
||||||
|
/ ___| / _ \| | (_) |_ ___
|
||||||
|
\___ \| | | | | | | __/ _ \
|
||||||
|
___) | |_| | |___| | || __/
|
||||||
|
|____/ \__\_\_____|_|\__\___|
|
||||||
|
|
||||||
|
Script to empty (delete all data from) sensor tables in the SQLite database
|
||||||
|
This script empties sensor data tables but preserves:
|
||||||
|
- timestamp_table
|
||||||
|
- config_table
|
||||||
|
- envea_sondes_table
|
||||||
|
- config_scripts_table
|
||||||
|
|
||||||
|
/usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/empty_sensor_tables.py
|
||||||
|
'''
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
|
def table_exists(cursor, table_name):
|
||||||
|
"""Check if a table exists in the database"""
|
||||||
|
try:
|
||||||
|
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
|
||||||
|
return cursor.fetchone() is not None
|
||||||
|
except sqlite3.Error as e:
|
||||||
|
print(f"[ERROR] Failed to check if table '{table_name}' exists: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_table_count(cursor, table_name):
|
||||||
|
"""Get the number of records in a table"""
|
||||||
|
try:
|
||||||
|
cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
|
||||||
|
return cursor.fetchone()[0]
|
||||||
|
except sqlite3.Error as e:
|
||||||
|
print(f"[WARNING] Could not get count for table '{table_name}': {e}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def empty_table(cursor, table_name):
|
||||||
|
"""Delete all records from a specific table"""
|
||||||
|
try:
|
||||||
|
# Get record count before deletion
|
||||||
|
initial_count = get_table_count(cursor, table_name)
|
||||||
|
|
||||||
|
if initial_count == 0:
|
||||||
|
print(f"[INFO] Table '{table_name}' is already empty")
|
||||||
|
return True, 0
|
||||||
|
|
||||||
|
# Delete all records
|
||||||
|
cursor.execute(f"DELETE FROM {table_name}")
|
||||||
|
deleted_count = cursor.rowcount
|
||||||
|
|
||||||
|
print(f"[SUCCESS] Deleted {deleted_count} records from '{table_name}'")
|
||||||
|
return True, deleted_count
|
||||||
|
|
||||||
|
except sqlite3.Error as e:
|
||||||
|
print(f"[ERROR] Failed to empty table '{table_name}': {e}")
|
||||||
|
return False, 0
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
result = {
|
||||||
|
'success': False,
|
||||||
|
'message': '',
|
||||||
|
'tables_processed': [],
|
||||||
|
'total_deleted': 0
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Connect to the SQLite database
|
||||||
|
print("[INFO] Connecting to database...")
|
||||||
|
conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db")
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Check database connection
|
||||||
|
cursor.execute("SELECT sqlite_version()")
|
||||||
|
version = cursor.fetchone()[0]
|
||||||
|
print(f"[INFO] Connected to SQLite version: {version}")
|
||||||
|
|
||||||
|
# List of sensor tables to empty (EXCLUDING timestamp_table and config tables)
|
||||||
|
sensor_tables = [
|
||||||
|
"data_NPM",
|
||||||
|
"data_NPM_5channels",
|
||||||
|
"data_BME280",
|
||||||
|
"data_envea",
|
||||||
|
"data_WIND",
|
||||||
|
"data_MPPT",
|
||||||
|
"data_NOISE",
|
||||||
|
"modem_status"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Tables to PRESERVE (not empty)
|
||||||
|
preserved_tables = [
|
||||||
|
"timestamp_table",
|
||||||
|
"config_table",
|
||||||
|
"envea_sondes_table",
|
||||||
|
"config_scripts_table"
|
||||||
|
]
|
||||||
|
|
||||||
|
print(f"[INFO] Will empty the following sensor tables: {', '.join(sensor_tables)}")
|
||||||
|
print(f"[INFO] Will preserve the following tables: {', '.join(preserved_tables)}")
|
||||||
|
|
||||||
|
# Check which tables actually exist
|
||||||
|
existing_tables = []
|
||||||
|
missing_tables = []
|
||||||
|
|
||||||
|
for table in sensor_tables:
|
||||||
|
if table_exists(cursor, table):
|
||||||
|
existing_tables.append(table)
|
||||||
|
record_count = get_table_count(cursor, table)
|
||||||
|
print(f"[INFO] Table '{table}' exists with {record_count} records")
|
||||||
|
else:
|
||||||
|
missing_tables.append(table)
|
||||||
|
print(f"[WARNING] Table '{table}' does not exist - skipping")
|
||||||
|
|
||||||
|
if missing_tables:
|
||||||
|
print(f"[INFO] Missing tables: {', '.join(missing_tables)}")
|
||||||
|
|
||||||
|
if not existing_tables:
|
||||||
|
result['success'] = True
|
||||||
|
result['message'] = "No sensor tables found to empty"
|
||||||
|
print("[WARNING] No sensor tables found to empty!")
|
||||||
|
print(json.dumps(result))
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Loop through existing tables and empty them
|
||||||
|
successful_operations = 0
|
||||||
|
failed_operations = 0
|
||||||
|
total_deleted = 0
|
||||||
|
|
||||||
|
for table in existing_tables:
|
||||||
|
success, deleted = empty_table(cursor, table)
|
||||||
|
if success:
|
||||||
|
successful_operations += 1
|
||||||
|
total_deleted += deleted
|
||||||
|
result['tables_processed'].append({
|
||||||
|
'name': table,
|
||||||
|
'deleted': deleted
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
failed_operations += 1
|
||||||
|
|
||||||
|
# Commit changes
|
||||||
|
print("[INFO] Committing changes...")
|
||||||
|
conn.commit()
|
||||||
|
print("[SUCCESS] Changes committed successfully!")
|
||||||
|
|
||||||
|
# Run VACUUM to optimize database space
|
||||||
|
if total_deleted > 0:
|
||||||
|
print("[INFO] Running VACUUM to optimize database space...")
|
||||||
|
try:
|
||||||
|
cursor.execute("VACUUM")
|
||||||
|
print("[SUCCESS] Database optimized successfully!")
|
||||||
|
except sqlite3.Error as e:
|
||||||
|
print(f"[WARNING] VACUUM failed: {e}")
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
print(f"\n[SUMMARY]")
|
||||||
|
print(f"Tables emptied successfully: {successful_operations}")
|
||||||
|
print(f"Tables with errors: {failed_operations}")
|
||||||
|
print(f"Tables skipped (missing): {len(missing_tables)}")
|
||||||
|
print(f"Total records deleted: {total_deleted}")
|
||||||
|
|
||||||
|
result['success'] = True
|
||||||
|
result['message'] = f"Successfully emptied {successful_operations} sensor tables. Total records deleted: {total_deleted}"
|
||||||
|
result['total_deleted'] = total_deleted
|
||||||
|
|
||||||
|
if failed_operations == 0:
|
||||||
|
print("[SUCCESS] All sensor tables emptied successfully!")
|
||||||
|
else:
|
||||||
|
result['message'] = f"Partial success: {successful_operations} tables emptied, {failed_operations} failed"
|
||||||
|
print("[WARNING] Some operations failed - check logs above")
|
||||||
|
|
||||||
|
# Output JSON result for web interface
|
||||||
|
print("\n[JSON_RESULT]")
|
||||||
|
print(json.dumps(result))
|
||||||
|
|
||||||
|
return failed_operations == 0
|
||||||
|
|
||||||
|
except sqlite3.Error as e:
|
||||||
|
result['message'] = f"Database error: {e}"
|
||||||
|
print(f"[ERROR] Database error: {e}")
|
||||||
|
print("\n[JSON_RESULT]")
|
||||||
|
print(json.dumps(result))
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
result['message'] = f"Unexpected error: {e}"
|
||||||
|
print(f"[ERROR] Unexpected error: {e}")
|
||||||
|
print("\n[JSON_RESULT]")
|
||||||
|
print(json.dumps(result))
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
# Always close the database connection
|
||||||
|
if 'conn' in locals():
|
||||||
|
conn.close()
|
||||||
|
print("[INFO] Database connection closed")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
success = main()
|
||||||
|
sys.exit(0 if success else 1)
|
||||||
Reference in New Issue
Block a user