express: time and session ID

Signed-off-by: Vincent Batts <vbatts@hashbangbash.com>
This commit is contained in:
Vincent Batts 2023-09-15 06:40:33 -04:00
parent c811d35fa5
commit 44167de812
Signed by: vbatts
GPG key ID: E30EFAA812C6E5ED
6 changed files with 1196 additions and 165 deletions

View file

@ -4,5 +4,8 @@
- https://github.com/TA3/web-user-behaviour - https://github.com/TA3/web-user-behaviour
- https://www.merriam-webster.com/dictionary/critter - https://www.merriam-webster.com/dictionary/critter
|| y/n | time to decide | indecision || || critter name | y/n | time to decide | indecision | session_id ||
| | | | | | | |

View file

@ -11,6 +11,10 @@
<button id="isNotCritterButton">Is <b>not</b> Critter</button> <button id="isNotCritterButton">Is <b>not</b> Critter</button>
<script> <script>
// Initialize session variables
let sessionStartTime;
let lastButtonClickTime;
// Function to generate a random session ID // Function to generate a random session ID
function generateSessionId() { function generateSessionId() {
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
@ -38,6 +42,21 @@
return sessionId; return sessionId;
} }
// Function to set session start time
function setSessionStartTime() {
sessionStartTime = new Date();
}
// Function to calculate and display time difference
function displayTimeDifference() {
if (sessionStartTime && lastButtonClickTime) {
const timeDifference = lastButtonClickTime - sessionStartTime;
console.log(`Time since session start: ${timeDifference} milliseconds`);
// You can display the time difference on the page as needed
}
}
// Add click event listeners to the buttons // Add click event listeners to the buttons
document.getElementById('isCritterButton').addEventListener('click', () => { document.getElementById('isCritterButton').addEventListener('click', () => {
recordButtonClick('is critter', getSessionId()); recordButtonClick('is critter', getSessionId());
@ -50,21 +69,29 @@
// Function to record button clicks on the server // Function to record button clicks on the server
async function recordButtonClick(buttonName, sessionId) { async function recordButtonClick(buttonName, sessionId) {
try { try {
await fetch('/recordButtonClick', { const currentTime = new Date();
method: 'POST', if (lastButtonClickTime) {
headers: { const timeDifference = currentTime - lastButtonClickTime;
'Content-Type': 'application/json', // Include the time difference in the POST request data
}, await fetch('/recordButtonClick', {
body: JSON.stringify({ buttonName, sessionId }), method: 'POST',
}); headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ buttonName, sessionId, timeDifference }),
});
}
lastButtonClickTime = currentTime; // Record the timestamp of the button click
displayTimeDifference(); // Calculate and display time difference
getRandomAnimal(); // Load another random animal getRandomAnimal(); // Load another random animal
} catch (error) { } catch (error) {
console.error('Error recording button click:', error); console.error('Error recording button click:', error);
} }
} }
// Initial random animal load // Initial random animal load and session start time
getRandomAnimal(); getRandomAnimal();
setSessionStartTime();
</script> </script>
</body> </body>
</html> </html>

1165
express/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -16,6 +16,6 @@
"dependencies": { "dependencies": {
"body-parser": "^1.20.2", "body-parser": "^1.20.2",
"express": "^4.18.2", "express": "^4.18.2",
"ioredis": "^5.3.2" "sqlite3": "^5.1.6"
} }
} }

View file

@ -1,71 +1,82 @@
const express = require('express'); const express = require('express');
const fs = require('fs');
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const Redis = require('ioredis'); const sqlite3 = require('sqlite3').verbose();
const app = express(); const app = express();
const port = 3000; const port = 3000;
// Create a Redis Client // Create an SQLite database and initialize tables
const redis = new Redis(); const db = new sqlite3.Database('mydb.db', (err) => {
if (err) {
console.error('Error opening SQLite database:', err.message);
} else {
console.log('Connected to SQLite database');
db.run(`
CREATE TABLE IF NOT EXISTS button_clicks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT,
button_name TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
time_difference INTEGER -- Add this column for time difference
)
`);
}
});
app.use(bodyParser.json()); app.use(bodyParser.json());
var animals;
// check and load animals into redis
fs.readFile("./animals.json", function (err, data) {
if (err) {
throw err;
}
var jsondata = JSON.parse(data);
animals = jsondata.animals;
//await redis.set('animals', jsondata.animals, 'EX', 3000);
});
// Serve the HTML file // Serve the HTML file
app.get('/', (req, res) => { app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html'); res.sendFile(__dirname + '/index.html');
}); });
// Route to get a random animal name // Route to get a random animal name
app.get('/getRandomAnimal', (req, res) => { app.get('/getRandomAnimal', async (req, res) => {
const randomAnimal = animals[Math.floor(Math.random() * animals.length)];
res.json({ animalName: randomAnimal });
});
// Route to record button clicks
app.post('/recordButtonClick', (req, res) => {
try {
const { buttonName, sessionId } = req.body;
// Use a Redis hash to store button clicks associated with session IDs
await redis.hincrby('buttonClicks', `${sessionId}:${buttonName}`, 1);
res.sendStatus(200);
if (buttonClicks.hasOwnProperty(buttonName)) {
buttonClicks[buttonName]++;
res.sendStatus(200);
} else {
res.status(400).json({ error: 'Invalid button name' });
}
} catch (error) {
console.error('Error recording button click:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Route to show the current results
app.get('/results', async (req, res) => {
try { try {
// Get the current random animal from Redis const animals = ['Dog', 'Cat', 'Elephant', 'Lion', 'Giraffe'];
const randomAnimal = await redis.get('randomAnimal'); const randomIndex = Math.floor(Math.random() * animals.length);
const randomAnimal = animals[randomIndex];
res.json({ animalName: randomAnimal });
} catch (error) {
console.error('Error fetching random animal:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Get the button click counts from the Redis hash // Route to record button clicks along with session IDs in SQLite
const buttonClicks = await redis.hgetall('buttonClicks'); app.post('/recordButtonClick', (req, res) => {
try {
const { buttonName, sessionId } = req.body;
res.json({ randomAnimal, buttonClicks }); db.run('INSERT INTO button_clicks (session_id, button_name) VALUES (?, ?)', [sessionId, buttonName], (err) => {
if (err) {
console.error('Error recording button click:', err.message);
res.status(500).json({ error: 'Internal server error' });
} else {
res.sendStatus(200);
}
});
} catch (error) {
console.error('Error recording button click:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Route to show the current results from SQLite
app.get('/results', (req, res) => {
try {
db.all('SELECT button_name, COUNT(*) as count FROM button_clicks GROUP BY button_name', (err, rows) => {
if (err) {
console.error('Error fetching results:', err.message);
res.status(500).json({ error: 'Internal server error' });
} else {
const results = {};
rows.forEach((row) => {
results[row.button_name] = row.count;
});
res.json(results);
}
});
} catch (error) { } catch (error) {
console.error('Error fetching results:', error); console.error('Error fetching results:', error);
res.status(500).json({ error: 'Internal server error' }); res.status(500).json({ error: 'Internal server error' });

View file

@ -1,31 +0,0 @@
<html>
<head>
<script src="https://cdn.jsdelivr.net/gh/TA3/web-user-behaviour/userBehaviour.min.js"></script>
<script src="https://unpkg.com/htmx.org@1.9.5" integrity="sha384-xcuj3WpfgjlKF+FXhSQFQ0ZNr39ln+hwjN3npfM9VBnUskLolQAcN80McRIVOPuO" crossorigin="anonymous"></script>
<script>
userBehaviour.config( {
userInfo: true,
clicks: true,
mouseMovement: true,
mouseMovementInterval: 1,
mouseScroll: true,
timeCount: true,
clearAfterProcess: true,
processTime: 15,
processData: function(results){
console.log(results);
},
});
userBehaviour.start();
</script>
</head>
<body>
booger snatch
<button hx-post="/clicked" hx-trigger="click" hx-target="#parent-div" hx-swap="outerHTML" >is critter</button>
<button hx-post="/clicked" hx-trigger="click" hx-target="#parent-div" hx-swap="outerHTML" >is not critter</button>
</body>
</html>