Add routes to get location and sensor by name

Added viewdata using chart.js
This commit is contained in:
newtbot 2024-01-29 00:44:10 +08:00
parent fadabf3451
commit c268e1d33d
7 changed files with 264 additions and 151 deletions

View File

@ -1,4 +1,5 @@
const {locationModel} = require("../database/model/locationModel"); const {locationModel} = require("../database/model/locationModel");
const {Op} = require("sequelize");
async function getLocation() { async function getLocation() {
const location = await locationModel.findAll(); const location = await locationModel.findAll();
@ -35,7 +36,32 @@ async function deleteLocation(id) {
}, },
}); });
} }
/*
const { Op } = require("sequelize");
var options = {
where: {
[Op.or]: [
{ 'subject': { [Op.like]: '%' + query + '%' } },
{ '$Comment.body$': { [Op.like]: '%' + query + '%' } }
]
},
include: [{ model: Comment }]
};
*/
async function getLocationByName(name) {
const location = await locationModel.findAll({
where: {
[Op.or]: [
{name: {[Op.like]: "%" + name + "%"}},
{added_by: {[Op.like]: "%" + name + "%"}},
{description: {[Op.like]: "%" + name + "%"}},
],
},
});
return location;
}
async function getLocationById(id) { async function getLocationById(id) {
const location = await locationModel.findAll({ const location = await locationModel.findAll({
@ -51,5 +77,6 @@ module.exports = {
addLocation, addLocation,
updateLocation, updateLocation,
deleteLocation, deleteLocation,
getLocationByName,
getLocationById, getLocationById,
}; };

View File

@ -1,4 +1,5 @@
const { sensorModel } = require("../database/model/sensorModel"); const { sensorModel } = require("../database/model/sensorModel");
const {Op} = require("sequelize");
async function getSensor() { async function getSensor() {
const sensor = await sensorModel.findAll(); const sensor = await sensorModel.findAll();
@ -55,6 +56,33 @@ async function deleteSensor(id) {
}, },
}); });
} }
/*
async function getLocationByName(name) {
const location = await locationModel.findAll({
where: {
[Op.or]: [
{name: {[Op.like]: "%" + name + "%"}},
{added_by: {[Op.like]: "%" + name + "%"}},
{description: {[Op.like]: "%" + name + "%"}},
],
},
});
return location;
}
*/
async function getSensorByName(name) {
const sensor = await sensorModel.findAll({
where: {
[Op.or]: [
{name: {[Op.like]: "%" + name + "%"}},
{added_by: {[Op.like]: "%" + name + "%"}},
{mac_address: {[Op.like]: "%" + name + "%"}},
{description: {[Op.like]: "%" + name + "%"}},
],
},
});
return sensor;
}
async function getSensorById(id) { async function getSensorById(id) {
const sensor = await sensorModel.findAll({ const sensor = await sensorModel.findAll({
@ -70,5 +98,6 @@ module.exports = {
addSensor, addSensor,
updateSensor, updateSensor,
deleteSensor, deleteSensor,
getSensorByName,
getSensorById, getSensorById,
}; };

View File

@ -1,7 +1,6 @@
//getting button from DOM id //getting button from DOM id
const buttons = document.querySelectorAll(".button-container button"); const buttons = document.querySelectorAll(".button-container button");
const queryButton = document.getElementById("querybutton-container"); const queryButton = document.getElementById("querybutton-container");
const ctx = document.getElementById("dataChart").getContext("2d");
$(document).ready(async function () { $(document).ready(async function () {
//https://stackoverflow.com/questions/9045868/javascript-date-getweek //https://stackoverflow.com/questions/9045868/javascript-date-getweek
@ -12,143 +11,109 @@ $(document).ready(async function () {
return Math.ceil(dayOfYear / 7); return Math.ceil(dayOfYear / 7);
}; };
let date = new Date(); let date = new Date();
var week = date.getWeek(); var week = date.getWeek() - 1; // Subtracting 1 to get the actual week number
var today = date.getDate(); var today = date.getDate();
var month = date.getMonth() + 1; // Adding 1 to get the actual month number var month = date.getMonth() + 1; // Adding 1 to get the actual month number
var year = date.getFullYear(); var year = date.getFullYear();
//year data
app.api.get("sensor-data/data?year=" + year, function (error, data) {
//console.log(data);
});
//monthly data
app.api.get("sensor-data/data?month=" + month, function (error, data) {
//console.log(data);
});
//weekly data
app.api.get(
"sensor-data/data?month=" + month + "&week=" + week,
function (error, data) {
//console.log(data);
}
);
//daily data
app.api.get(
"sensor-data/data?month=" + month + "&week=" + week + "&day=" + today,
function (error, data) {
for (let rows of data) {
console.log(rows);
getDate(rows.CreatedAt);
} // Initialize initialData for chart
} const initialData = {
); labels: [], // Array to store timestamps
}); datasets: [
{
label: "Average MeasurementData",
data: [], // Array to store measurements objects
backgroundColor: "green",
borderColor: "green",
},
],
};
function getDate(date){ // Create Chart.js chart
console.log(date); const ctx = document.getElementById("DailyDataChart").getContext("2d");
const chart = new Chart(ctx, {
} type: "bar",
data: initialData,
/* options: {
responsive: true,
(async function() { title: {
const data = [ display: true,
{ year: 2010, count: 10 }, text: "Average measurement metric data by Hour",
{ year: 2011, count: 20 },
{ year: 2012, count: 15 },
{ year: 2013, count: 25 },
{ year: 2014, count: 22 },
{ year: 2015, count: 30 },
{ year: 2016, count: 28 },
];
new Chart(
document.getElementById('acquisitions'),
{
type: 'bar',
data: {
labels: data.map(row => row.year),
datasets: [
{
label: 'Acquisitions by year',
data: data.map(row => row.count)
}
]
}
}
);
})();
*/
// Initial dataset (AQI)
const initialData = {
//date and time
labels: [ date.map(row => row.CreatedAt)],
datasets:[
{
//data like psi
label: "Average PSI Data",
data: [],
backgroundColor: "green",
borderColor: "green",
},
],
};
//setting chart
const chart = new Chart(ctx, {
type: "bar",
data: initialData,
options: {
responsive: true,
title: {
display: true,
text: "HISTORICAL",
},
subtitle: {
display: true,
text: "Historic air quality graph for Singapore",
},
legend: {
display: false,
},
tooltips: {
mode: "index",
intersect: false,
callbacks: {
label: function (tooltipItem, data) {
const label = data.labels[tooltipItem.index];
return label + ": " + data.datasets[0].data[tooltipItem.index];
},
}, },
}, },
scales: { });
xAxes: [
{ // Function to update chart data based on button clicked
barPercentage: 0.6, function updateChart(metric) {
categoryPercentage: 0.7, const queryParams = `sensor-data/data?month=${month}&week=${week}&day=${today}`;
ticks: { app.api.get(queryParams, function (error, data) {
autoSkip: true, // Clear previous data
}, initialData.labels = []; //timestamp
maxRotation: 0, initialData.datasets[0].data = []; //measurement data dependinbg on metric
minRotation: 0,
}, // Group data by hour and calculate average value
], const hourlyData = {};
yAxes: [ for (let row of data) {
{ //each row contains a timestamp and measurement data of each sensor and location
title: { const createdAt = new Date(row.createdAt); //set to local time
display: true, const hourString = new Date(
text: "Value", createdAt.getFullYear(),
}, createdAt.getMonth(),
}, createdAt.getDate(),
], createdAt.getHours()
}, ).toISOString();
}, if (!hourlyData[hourString]) {
hourlyData[hourString] = [];
}
hourlyData[hourString].push(row.measurement[metric]); //pushing measurement data into hourlyData
}
// Calculate average value for each hour
//console.log(hourlyData); //24 values for each hour of the day
for (let hourString in hourlyData) {
const averageValue =
hourlyData[hourString].reduce((acc, val) => acc + val, 0) /
hourlyData[hourString].length;
initialData.labels.push(
new Date(hourString).toLocaleString("en-US", {
timeZone: "UTC",
hour12: false,
})
);
initialData.datasets[0].data.push(averageValue);
}
// Update chart
chart.update();
});
}
// Event listeners for buttons
document.getElementById("psiButton").addEventListener("click", function () {
updateChart("psi");
});
document.getElementById("tempButton").addEventListener("click", function () {
updateChart("temperature");
});
document.getElementById("humButton").addEventListener("click", function () {
updateChart("humidity");
});
document.getElementById("o3Button").addEventListener("click", function () {
updateChart("o3");
});
document.getElementById("no2Button").addEventListener("click", function () {
updateChart("no2");
});
document.getElementById("so2Button").addEventListener("click", function () {
updateChart("so2");
});
document.getElementById("coButton").addEventListener("click", function () {
updateChart("co");
});
}); });
queryButton.addEventListener("click", (event) => {
const clickedButton = event.target;
//console.log(clickedButton.id);
//make it switch bar chart based on query button
});

View File

@ -2,6 +2,7 @@ const {
addLocation, addLocation,
getLocation, getLocation,
getLocationById, getLocationById,
getLocationByName,
updateLocation, updateLocation,
deleteLocation, deleteLocation,
} = require("../functions/location"); } = require("../functions/location");
@ -58,6 +59,19 @@ router.delete("/delete", async (req, res, next) => {
} }
}); });
//get location by name
router.get("/name/:name", async (req, res, next) => {
try {
//get params
const { name } = req.params;
const location = await getLocationByName(name);
res.status(200).json(location);
} catch (error) {
console.error(error);
next(error);
}
});
//get location by id //get location by id

View File

@ -3,6 +3,7 @@ const {
addSensor, addSensor,
updateSensor, updateSensor,
deleteSensor, deleteSensor,
getSensorByName,
getSensorById getSensorById
} = require("../functions/sensor.js"); } = require("../functions/sensor.js");
@ -52,6 +53,16 @@ router.delete("/delete", async (req, res, next) => {
next(error); next(error);
} }
}); });
router.get("/name/:name", async (req, res, next) => {
try {
const sensor = await getSensorByName(req.params.name);
res.status(200).json(sensor);
} catch (error) {
console.error(error);
next(error);
}
});
router.get("/:id", async (req, res, next) => { router.get("/:id", async (req, res, next) => {
try { try {
const sensor = await getSensorById(req.params.id); const sensor = await getSensorById(req.params.id);

View File

@ -42,12 +42,13 @@
</script> </script>
<!-- socket.io scriot --> <!-- socket.io scriot -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.2.0/socket.io.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.2.0/socket.io.min.js"></script>
<!-- fancy table cdn -->
<!-- <script src="https://cdn.jsdelivr.net/npm/jquery.fancytable/dist/fancyTable.min.js"></script> -->
<!-- jq-repeat --> <!-- jq-repeat -->
<script src="js/jq-repeat.js"></script> <script src="js/jq-repeat.js"></script>
<!-- jquery app.js --> <!-- jquery public app.js -->
<script src="js/app.js"></script> <script src="js/app.js"></script>
</head> </head>
<!-- javascript function to check if user is auth --> <!-- javascript function to check if user is auth -->
@ -99,12 +100,6 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/contact">Contact</a> <a class="nav-link" href="/contact">Contact</a>
</li> </li>
<!--
<li class="nav-item">
<a class="nav-link" href="/api">API Doc</a>
</li>
-->
<!-- profile button --> <!-- profile button -->
<div class="form-inline mt-2 mt-md-0"> <div class="form-inline mt-2 mt-md-0">
<a id="cl-viewdata-button" class="btn btn-outline-info btn-sm my-2 my-sm-0" href="/viewdata" <a id="cl-viewdata-button" class="btn btn-outline-info btn-sm my-2 my-sm-0" href="/viewdata"

View File

@ -5,6 +5,26 @@
<script type="text/javascript"> <script type="text/javascript">
// Require login to see this page. // Require login to see this page.
app.auth.forceLogin() app.auth.forceLogin()
//get Location data
app.api.get('location', function (error, data) {
for (let row in data){
//format time to local time than push
data[row].createdAt = new Date(data[row].createdAt).toLocaleString();
data[row].updatedAt = new Date(data[row].updatedAt).toLocaleString();
$.scope.LocationTable.push(data[row]);
}
})
//get sensor data
app.api.get('sensor', function (error, data) {
for (let row in data){
//format time to local time than push
data[row].createdAt = new Date(data[row].createdAt).toLocaleString();
data[row].updatedAt = new Date(data[row].updatedAt).toLocaleString();
$.scope.SensorTable.push(data[row]);
}
})
</script> </script>
<br> <br>
<br> <br>
@ -12,17 +32,15 @@
<body> <body>
<div class="air-quality-container"> <div class="air-quality-container">
<div class="querybutton-container" id="querybutton-container"> <!-- header -->
<!-- <button id="yearButton">Year</button> <div class="header-container">
<button id="monthButton">Month</button> <h1>Daily Air Quality Data Chart</h1>
<button id="weekButton">Week</button> -->
<button id="dayButton">Day</button>
</div> </div>
<br> <br>
<div class="chart-container"> <div class="chart-container">
<canvas id="dataChart"></canvas> <canvas id="DailyDataChart"></canvas>
</div> </div>
<!-- <div class="button-container"> <div class="button-container">
<button id="psiButton">PSI</button> <button id="psiButton">PSI</button>
<button id="tempButton">Temperature</button> <button id="tempButton">Temperature</button>
<button id="humButton">Humidity</button> <button id="humButton">Humidity</button>
@ -31,16 +49,70 @@
<button id="so2Button">SO2</button> <button id="so2Button">SO2</button>
<button id="coButton">CO</button> <button id="coButton">CO</button>
</div> </div>
--> <br>
<div class="header-container">
<h1>Location Table</h1>
</div>
<table id="LocationTable" class="table table-striped LocationTable">
<thead>
<tr>
<th>ID</th>
<th>Location Name</th>
<th>Added_By</th>
<th>Description</th>
<th>CreatedAt</th>
<th>UpdatedAt</th>
</tr>
</thead>
<tbody>
<!-- Table Content Goes Here -->
<tr jq-repeat="LocationTable">
<td>{{id}}</td>
<td>{{name}}</td>
<td>{{added_by}}</td>
<td>{{description}}</td>
<td>{{createdAt}}</td>
<td>{{updatedAt}}</td>
</tbody>
</table>
<div class="header-container">
<h1>Sensor Table</h1>
</div>
<table id="sensorTable" class="table table-striped sensorTable">
<thead>
<tr>
<th>ID</th>
<th>Sensor Name</th>
<th>Added_By</th>
<th>Mac Address</th>
<th>Description</th>
<th>Location ID</th>
<th>CreatedAt</th>
<th>UpdatedAt</th>
</tr>
</thead>
<tbody>
<!-- Table Content Goes Here -->
<tr jq-repeat="SensorTable">
<td>{{id}}</td>
<td>{{name}}</td>
<td>{{added_by}}</td>
<td>{{mac_address}}</td>
<td>{{description}}</td>
<td>{{locationid}}</td>
<td>{{createdAt}}</td>
<td>{{updatedAt}}</td>
</tbody>
</table>
<br> <br>
<div class="download-container"> <div class="download-container">
<p>Download data here:</p> <p>Download sensor data here:</p>
<button id="downloadCSVButton" onclick="downloadCSV()">Download CSV</button> <button id="downloadCSVButton" onclick="downloadCSV()">Download CSV</button>
<br><br> <br><br>
<button id="downloadCSVButton" onclick="downloadExcel()">Download Excel Sheet</button> <button id="downloadCSVButton" onclick="downloadExcel()">Download Excel Sheet</button>
</div> </div>
</div> </div>
</body> </body>
<br> <br>
<br> <br>