Merge pull request #9 from Newtbot/update-with-dashboard-data
update with dashboard data
This commit is contained in:
commit
ce1c44556f
@ -40,7 +40,14 @@ const connection = mysql.createConnection({
|
|||||||
timezone: "Z", // Set the timezone to UTC
|
timezone: "Z", // Set the timezone to UTC
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const connection2 = mysql.createConnection({
|
||||||
module.exports = { connection };
|
host: process.env.host,
|
||||||
|
user: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASS,
|
||||||
|
database: "eco_saver",
|
||||||
|
timezone: "Z", // Set the timezone to UTC
|
||||||
|
});
|
||||||
|
module.exports = { connection,connection2 };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ const validator = require('validator');
|
|||||||
|
|
||||||
const { transporter } = require("./modules/nodeMailer");
|
const { transporter } = require("./modules/nodeMailer");
|
||||||
const { connection } = require("./modules/mysql");
|
const { connection } = require("./modules/mysql");
|
||||||
|
const { connection2 } = require("./modules/mysql");
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
@ -207,28 +207,40 @@ const logActivity = async (username, success, message) => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Update your /home route to retrieve the overall last 10 logins for all users
|
app.get("/home", isAuthenticated, (req, res) => {
|
||||||
app.get("/home", isAuthenticated, (req, res) => {
|
|
||||||
// Retrieve the overall last 10 logins for all users
|
|
||||||
const loginsQuery =
|
|
||||||
"SELECT username, lastLogin FROM users ORDER BY lastLogin DESC LIMIT 10";
|
|
||||||
connection.query(loginsQuery, (error, loginResults) => {
|
|
||||||
if (error) {
|
|
||||||
console.error("Error executing login logs query:", error);
|
|
||||||
res.status(500).send("Internal Server Error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log the results on the server side
|
// Retrieve the last 10 sensor data records from the sensordata table
|
||||||
console.log("Login Logs on Server:", loginResults);
|
const sensorDataQuery =
|
||||||
|
"SELECT locationid, measurement, createdAt FROM sensordata ORDER BY createdAt DESC LIMIT 10";
|
||||||
|
|
||||||
// Render the home page with login logs data
|
connection2.query(sensorDataQuery, (error, sensorDataResults) => {
|
||||||
res.render("home", {
|
if (error) {
|
||||||
username: req.session.username,
|
console.error("Error executing sensor data query:", error);
|
||||||
loginLogs: loginResults,
|
res.status(500).send("Internal Server Error");
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log the results on the server side
|
||||||
|
|
||||||
|
|
||||||
|
// Render the home page with sensor data
|
||||||
|
res.render("home", {
|
||||||
|
username: req.session.username,
|
||||||
|
sensorData: sensorDataResults,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
app.get('/api/locations', (req, res) => {
|
||||||
|
connection2.query('SELECT `id`, `name` FROM `locations`', (error, results) => {
|
||||||
|
if (error) {
|
||||||
|
console.error('Error fetching locations:', error);
|
||||||
|
res.status(500).json({ error: 'Internal Server Error' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json(results);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/inusers", isAuthenticated, (req, res) => {
|
app.get("/inusers", isAuthenticated, (req, res) => {
|
||||||
// Fetch all user data from the database
|
// Fetch all user data from the database
|
||||||
const allUsersQuery = "SELECT * FROM users";
|
const allUsersQuery = "SELECT * FROM users";
|
||||||
|
@ -8,7 +8,15 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Home</title>
|
<title>Home</title>
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
<style>
|
<style>
|
||||||
|
.chart-container {
|
||||||
|
width: 400px;
|
||||||
|
height: 250px;
|
||||||
|
margin: 20px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: 'Arial', sans-serif;
|
font-family: 'Arial', sans-serif;
|
||||||
@ -73,6 +81,7 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
|
||||||
<div id="navbar">
|
<div id="navbar">
|
||||||
<h1>Eco Saver</h1>
|
<h1>Eco Saver</h1>
|
||||||
@ -84,25 +93,154 @@
|
|||||||
|
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<h2>Welcome to the Home Page, <%= username %>!</h2>
|
<h2>Welcome to the Home Page, <%= username %>!</h2>
|
||||||
<h3>Last 10 Logins:</h3>
|
<h3>Last 10 Sensor Data Records:</h3>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Username</th>
|
<th>Location</th>
|
||||||
<th>Last Login Time</th>
|
<th>CO</th>
|
||||||
|
<th>O3</th>
|
||||||
|
<th>NO2</th>
|
||||||
|
<th>PSI</th>
|
||||||
|
<th>SO2</th>
|
||||||
|
<th>Humidity</th>
|
||||||
|
<th>Wind Speed</th>
|
||||||
|
<th>Temperature</th>
|
||||||
|
<th>Created At</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<% loginLogs.forEach(log => { %>
|
<% sensorData.forEach(data => { %>
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= log.username %></td>
|
<td><%= data.locationid %></td>
|
||||||
<td><%= new Date(log.lastLogin).toLocaleString('en-US', { timeZone: 'Asia/Singapore' }) %></td>
|
<td><%= data.measurement.co %></td>
|
||||||
|
<td><%= data.measurement.o3 %></td>
|
||||||
|
<td><%= data.measurement.no2 %></td>
|
||||||
|
<td><%= data.measurement.psi %></td>
|
||||||
|
<td><%= data.measurement.so2 %></td>
|
||||||
|
<td><%= data.measurement.humidity %></td>
|
||||||
|
<td><%= data.measurement.windspeed %></td>
|
||||||
|
<td><%= data.measurement.temperature %></td>
|
||||||
|
<td><%= new Date(data.createdAt).toLocaleString('en-US', { timeZone: 'Asia/Singapore' }) %></td>
|
||||||
</tr>
|
</tr>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", async function () {
|
||||||
|
console.log("DOM Loaded");
|
||||||
|
|
||||||
|
// Extract data from sensorData
|
||||||
|
const sensorData = JSON.parse('<%- JSON.stringify(sensorData) %>');
|
||||||
|
console.log("Sensor Data:", sensorData);
|
||||||
|
|
||||||
|
// Fetch location names from the server
|
||||||
|
const locationNames = await fetch('/api/locations') // Adjust the API endpoint
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => data.map(location => ({ id: location.id, name: location.name })))
|
||||||
|
.catch(error => console.error('Error fetching location names:', error));
|
||||||
|
|
||||||
|
// Group sensorData by locationid
|
||||||
|
const groupedData = groupBy(sensorData, 'locationid');
|
||||||
|
|
||||||
|
// Get the content div
|
||||||
|
const contentDiv = document.getElementById('content');
|
||||||
|
|
||||||
|
// Create a chart for each location
|
||||||
|
Object.keys(groupedData).forEach(locationId => {
|
||||||
|
const locationData = groupedData[locationId];
|
||||||
|
|
||||||
|
// Find the corresponding location name
|
||||||
|
const locationName = locationNames.find(location => location.id === parseInt(locationId, 10))?.name || `Unknown Location ${locationId}`;
|
||||||
|
|
||||||
|
// Create a container for the chart
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.className = 'chart-container';
|
||||||
|
|
||||||
|
// Create a title for the container with location name
|
||||||
|
const title = document.createElement('h4');
|
||||||
|
title.textContent = `Location: ${locationName}`;
|
||||||
|
container.appendChild(title);
|
||||||
|
|
||||||
|
// Get labels (Location IDs)
|
||||||
|
const labels = locationData.map(data => new Date(data.createdAt).toLocaleString('en-US', { timeZone: 'Asia/Singapore' }));
|
||||||
|
|
||||||
|
// Create datasets for each measurement
|
||||||
|
const datasets = [
|
||||||
|
{
|
||||||
|
label: 'CO',
|
||||||
|
data: locationData.map(data => data.measurement.co),
|
||||||
|
backgroundColor: 'rgba(255, 99, 132, 0.5)', // Red color
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'O3',
|
||||||
|
data: locationData.map(data => data.measurement.o3),
|
||||||
|
backgroundColor: 'rgba(54, 162, 235, 0.5)', // Blue color
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'NO2',
|
||||||
|
data: locationData.map(data => data.measurement.no2),
|
||||||
|
backgroundColor: 'rgba(255, 206, 86, 0.5)', // Yellow color
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'SO2',
|
||||||
|
data: locationData.map(data => data.measurement.so2),
|
||||||
|
backgroundColor: 'rgba(75, 192, 192, 0.5)', // Green color
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Create a canvas element for each location
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 400;
|
||||||
|
canvas.height = 200;
|
||||||
|
|
||||||
|
// Append canvas to the container
|
||||||
|
container.appendChild(canvas);
|
||||||
|
|
||||||
|
// Append container to the content div
|
||||||
|
contentDiv.appendChild(container);
|
||||||
|
|
||||||
|
// Create a bar chart for each location
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: datasets,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
beginAtZero: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper function to group data by a specified key
|
||||||
|
function groupBy(arr, key) {
|
||||||
|
return arr.reduce((acc, obj) => {
|
||||||
|
const groupKey = obj[key];
|
||||||
|
acc[groupKey] = acc[groupKey] || [];
|
||||||
|
acc[groupKey].push(obj);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
107
Sean/views/home.js
Normal file
107
Sean/views/home.js
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
document.addEventListener("DOMContentLoaded", async function () {
|
||||||
|
console.log("DOM Loaded");
|
||||||
|
|
||||||
|
// Extract data from sensorData
|
||||||
|
const sensorData = JSON.parse('<%- JSON.stringify(sensorData) %>');
|
||||||
|
console.log("Sensor Data:", sensorData);
|
||||||
|
|
||||||
|
// Fetch location names from the server
|
||||||
|
const locationNames = await fetch('/api/locations') // Adjust the API endpoint
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => data.map(location => ({ id: location.id, name: location.name })))
|
||||||
|
.catch(error => console.error('Error fetching location names:', error));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Group sensorData by locationid
|
||||||
|
const groupedData = groupBy(sensorData, 'locationid');
|
||||||
|
|
||||||
|
// Get the content div
|
||||||
|
const contentDiv = document.getElementById('content');
|
||||||
|
|
||||||
|
// Create a chart for each location
|
||||||
|
Object.keys(groupedData).forEach(locationId => {
|
||||||
|
const locationData = groupedData[locationId];
|
||||||
|
|
||||||
|
// Find the corresponding location name
|
||||||
|
const locationName = locationNames.find(location => location.id === parseInt(locationId, 10))?.name || `Unknown Location ${locationId}`;
|
||||||
|
|
||||||
|
// Create a container for the chart
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.className = 'chart-container';
|
||||||
|
|
||||||
|
// Create a title for the container with location name
|
||||||
|
const title = document.createElement('h4');
|
||||||
|
title.textContent = `Location: ${locationName}`;
|
||||||
|
container.appendChild(title);
|
||||||
|
|
||||||
|
// Get labels (Location IDs)
|
||||||
|
const labels = locationData.map(data => new Date(data.createdAt).toLocaleString('en-US', { timeZone: 'Asia/Singapore' }));
|
||||||
|
|
||||||
|
// Create datasets for each measurement
|
||||||
|
const datasets = [
|
||||||
|
{
|
||||||
|
label: 'CO',
|
||||||
|
data: locationData.map(data => data.measurement.co),
|
||||||
|
backgroundColor: 'rgba(255, 99, 132, 0.5)', // Red color
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'O3',
|
||||||
|
data: locationData.map(data => data.measurement.o3),
|
||||||
|
backgroundColor: 'rgba(54, 162, 235, 0.5)', // Blue color
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'NO2',
|
||||||
|
data: locationData.map(data => data.measurement.no2),
|
||||||
|
backgroundColor: 'rgba(255, 206, 86, 0.5)', // Yellow color
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'SO2',
|
||||||
|
data: locationData.map(data => data.measurement.so2),
|
||||||
|
backgroundColor: 'rgba(75, 192, 192, 0.5)', // Green color
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Create a canvas element for each location
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 400;
|
||||||
|
canvas.height = 200;
|
||||||
|
|
||||||
|
// Append canvas to the container
|
||||||
|
container.appendChild(canvas);
|
||||||
|
|
||||||
|
// Append container to the content div
|
||||||
|
contentDiv.appendChild(container);
|
||||||
|
|
||||||
|
// Create a bar chart for each location
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: datasets,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
beginAtZero: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper function to group data by a specified key
|
||||||
|
function groupBy(arr, key) {
|
||||||
|
return arr.reduce((acc, obj) => {
|
||||||
|
const groupKey = obj[key];
|
||||||
|
acc[groupKey] = acc[groupKey] || [];
|
||||||
|
acc[groupKey].push(obj);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user