314 lines
10 KiB
SQL
314 lines
10 KiB
SQL
-- Employee Workstation Activity Tracking System
|
|
-- Database Schema Creation Script
|
|
|
|
-- Instructions:
|
|
-- 1. For SQLite: sqlite3 work_events.db < create_db.sql
|
|
-- 2. For PostgreSQL: psql -U username -d database_name -f create_db.sql
|
|
|
|
-- Drop existing table if it exists
|
|
DROP TABLE IF EXISTS work_events;
|
|
|
|
-- Create the work_events table
|
|
CREATE TABLE work_events (
|
|
id SERIAL PRIMARY KEY,
|
|
"user" VARCHAR(100) NOT NULL, -- Note the double quotes
|
|
state VARCHAR(10) NOT NULL CHECK (state IN ('working', 'stopped')),
|
|
ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Create indexes for performance optimization
|
|
CREATE INDEX idx_work_events_user ON work_events("user");
|
|
CREATE INDEX idx_work_events_ts ON work_events(ts);
|
|
CREATE INDEX idx_work_events_user_state ON work_events("user", state);
|
|
|
|
-- Optional: Sample data for testing (comment out in production)
|
|
|
|
-- Clear previous sample data if necessary before inserting new data
|
|
-- DELETE FROM work_events WHERE "user" LIKE 'User %' OR "user" LIKE 'user%';
|
|
|
|
-- Remove older generic sample data
|
|
/*
|
|
INSERT INTO work_events ("user", state, ts) VALUES
|
|
('user1', 'working', CURRENT_TIMESTAMP - INTERVAL '2 hours'),
|
|
('user1', 'stopped', CURRENT_TIMESTAMP - INTERVAL '1 hour'),
|
|
('user1', 'working', CURRENT_TIMESTAMP - INTERVAL '30 minutes'),
|
|
('user2', 'working', CURRENT_TIMESTAMP - INTERVAL '3 hours'),
|
|
('user2', 'stopped', CURRENT_TIMESTAMP - INTERVAL '2 hours 30 minutes'),
|
|
('user2', 'working', CURRENT_TIMESTAMP - INTERVAL '2 hours'),
|
|
('user2', 'stopped', CURRENT_TIMESTAMP - INTERVAL '1 hour');
|
|
*/
|
|
|
|
-- Example queries for reporting
|
|
|
|
-- 1. Daily activity summary per user
|
|
|
|
SELECT
|
|
"user",
|
|
DATE(ts) AS day,
|
|
COUNT(*) AS event_count,
|
|
SUM(CASE WHEN state = 'working' THEN 1 ELSE 0 END) AS working_events
|
|
FROM work_events
|
|
GROUP BY "user", day
|
|
ORDER BY day DESC, "user";
|
|
|
|
|
|
-- 2. Weekly activity summary
|
|
|
|
SELECT
|
|
"user",
|
|
date_trunc('week', ts) AS week_start,
|
|
COUNT(*) AS event_count
|
|
FROM work_events
|
|
GROUP BY "user", week_start
|
|
ORDER BY week_start DESC, "user";
|
|
|
|
|
|
-- 3. Monthly activity summary
|
|
|
|
SELECT
|
|
"user",
|
|
date_trunc('month', ts) AS month_start,
|
|
COUNT(*) AS event_count
|
|
FROM work_events
|
|
GROUP BY "user", month_start
|
|
ORDER BY month_start DESC, "user";
|
|
|
|
|
|
-- Notes on data retention:
|
|
-- According to requirements, raw event logs should be retained for 1 year.
|
|
-- Setup a periodic cleanup job to remove old data:
|
|
|
|
-- PostgreSQL:
|
|
|
|
DELETE FROM work_events
|
|
WHERE ts < CURRENT_TIMESTAMP - INTERVAL '1 year';
|
|
|
|
|
|
-- SQLite:
|
|
/*
|
|
DELETE FROM work_events
|
|
WHERE ts < datetime('now', '-1 year');
|
|
*/
|
|
|
|
-- Enhanced Sample Data Generation (Relative to assumed date 2025-05-04)
|
|
-- Ensures Users A-E have data for Today, This Week, and This Month (April)
|
|
|
|
|
|
|
|
-- Sample data for real users: Robert, Ilia, and Nika
|
|
-- Robert: Working pattern for today, this week, and this month
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Robert', 'working', '2025-05-04 08:30:00'); -- Today
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Robert', 'stopped', '2025-05-04 12:30:00');
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Robert', 'working', '2025-05-04 13:15:00');
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Robert', 'stopped', '2025-05-04 17:00:00');
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Robert', 'working', '2025-05-02 08:45:00'); -- This Week
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Robert', 'stopped', '2025-05-02 16:15:00');
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Robert', 'working', '2025-04-15 09:00:00'); -- This Month
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Robert', 'stopped', '2025-04-15 17:30:00');
|
|
|
|
-- Ilia: Working pattern for today, this week, and this month
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Ilia', 'working', '2025-05-04 09:00:00'); -- Today
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Ilia', 'stopped', '2025-05-04 13:00:00');
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Ilia', 'working', '2025-05-04 14:00:00');
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Ilia', 'stopped', '2025-05-04 18:00:00');
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Ilia', 'working', '2025-05-03 09:30:00'); -- This Week
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Ilia', 'stopped', '2025-05-03 17:30:00');
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Ilia', 'working', '2025-04-20 08:30:00'); -- This Month
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Ilia', 'stopped', '2025-04-20 16:30:00');
|
|
|
|
-- Nika: Working pattern for today, this week, and this month
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Nika', 'working', '2025-05-04 08:15:00'); -- Today
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Nika', 'stopped', '2025-05-04 12:00:00');
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Nika', 'working', '2025-05-04 12:45:00');
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Nika', 'stopped', '2025-05-04 16:30:00');
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Nika', 'working', '2025-05-01 08:30:00'); -- This Week
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Nika', 'stopped', '2025-05-01 16:45:00');
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Nika', 'working', '2025-04-10 09:15:00'); -- This Month
|
|
INSERT INTO work_events ("user", state, ts) VALUES ('Nika', 'stopped', '2025-04-10 17:15:00');
|
|
|
|
-- Enhanced SQL queries for reporting with duration calculations
|
|
|
|
-- 4. Daily working duration per user (for dashboard)
|
|
-- This calculates the total working hours per day by matching working->stopped pairs
|
|
SELECT
|
|
w1."user",
|
|
DATE(w1.ts) AS day,
|
|
SUM(
|
|
EXTRACT(EPOCH FROM (w2.ts - w1.ts))/3600
|
|
) AS duration_hours,
|
|
MIN(CASE WHEN w1.state = 'working' THEN w1.ts END) AS first_login_time
|
|
FROM
|
|
work_events w1
|
|
JOIN
|
|
work_events w2 ON w1."user" = w2."user"
|
|
AND w1.state = 'working'
|
|
AND w2.state = 'stopped'
|
|
AND w2.ts > w1.ts
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM work_events w3
|
|
WHERE w3."user" = w1."user"
|
|
AND w3.ts > w1.ts AND w3.ts < w2.ts
|
|
)
|
|
GROUP BY
|
|
w1."user", day
|
|
ORDER BY
|
|
day DESC, w1."user";
|
|
|
|
-- 5. Weekly working duration per user (for dashboard)
|
|
SELECT
|
|
w1."user",
|
|
date_trunc('week', w1.ts) AS week_start,
|
|
SUM(
|
|
EXTRACT(EPOCH FROM (w2.ts - w1.ts))/3600
|
|
) AS duration_hours,
|
|
MIN(CASE WHEN w1.state = 'working' THEN w1.ts END) AS first_login_time
|
|
FROM
|
|
work_events w1
|
|
JOIN
|
|
work_events w2 ON w1."user" = w2."user"
|
|
AND w1.state = 'working'
|
|
AND w2.state = 'stopped'
|
|
AND w2.ts > w1.ts
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM work_events w3
|
|
WHERE w3."user" = w1."user"
|
|
AND w3.ts > w1.ts AND w3.ts < w2.ts
|
|
)
|
|
GROUP BY
|
|
w1."user", week_start
|
|
ORDER BY
|
|
week_start DESC, w1."user";
|
|
|
|
-- 6. Monthly working duration per user (for dashboard)
|
|
SELECT
|
|
w1."user",
|
|
date_trunc('month', w1.ts) AS month_start,
|
|
SUM(
|
|
EXTRACT(EPOCH FROM (w2.ts - w1.ts))/3600
|
|
) AS duration_hours,
|
|
MIN(CASE WHEN w1.state = 'working' THEN w1.ts END) AS first_login_time
|
|
FROM
|
|
work_events w1
|
|
JOIN
|
|
work_events w2 ON w1."user" = w2."user"
|
|
AND w1.state = 'working'
|
|
AND w2.state = 'stopped'
|
|
AND w2.ts > w1.ts
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM work_events w3
|
|
WHERE w3."user" = w1."user"
|
|
AND w3.ts > w1.ts AND w3.ts < w2.ts
|
|
)
|
|
GROUP BY
|
|
w1."user", month_start
|
|
ORDER BY
|
|
month_start DESC, w1."user";
|
|
|
|
-- 7. Detailed user activity log for specific user and date range
|
|
-- This query shows all work sessions with start, end times and duration
|
|
-- Use :username and :start_date, :end_date as parameters
|
|
SELECT
|
|
w1."user",
|
|
DATE(w1.ts) AS work_date,
|
|
w1.ts AS start_time,
|
|
w2.ts AS end_time,
|
|
EXTRACT(EPOCH FROM (w2.ts - w1.ts))/3600 AS session_duration_hours
|
|
FROM
|
|
work_events w1
|
|
JOIN
|
|
work_events w2 ON w1."user" = w2."user"
|
|
AND w1.state = 'working'
|
|
AND w2.state = 'stopped'
|
|
AND w2.ts > w1.ts
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM work_events w3
|
|
WHERE w3."user" = w1."user"
|
|
AND w3.ts > w1.ts AND w3.ts < w2.ts
|
|
)
|
|
WHERE
|
|
w1."user" = :username
|
|
AND DATE(w1.ts) BETWEEN :start_date AND :end_date
|
|
ORDER BY
|
|
w1.ts;
|
|
|
|
-- 8. Filter for specific time periods (Today, This Week, This Month)
|
|
-- Today's work duration by user
|
|
SELECT
|
|
w1."user",
|
|
CURRENT_DATE AS day,
|
|
SUM(
|
|
EXTRACT(EPOCH FROM (w2.ts - w1.ts))/3600
|
|
) AS duration_hours,
|
|
MIN(CASE WHEN w1.state = 'working' THEN w1.ts END) AS first_login_time
|
|
FROM
|
|
work_events w1
|
|
JOIN
|
|
work_events w2 ON w1."user" = w2."user"
|
|
AND w1.state = 'working'
|
|
AND w2.state = 'stopped'
|
|
AND w2.ts > w1.ts
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM work_events w3
|
|
WHERE w3."user" = w1."user"
|
|
AND w3.ts > w1.ts AND w3.ts < w2.ts
|
|
)
|
|
WHERE
|
|
DATE(w1.ts) = CURRENT_DATE
|
|
GROUP BY
|
|
w1."user"
|
|
ORDER BY
|
|
w1."user";
|
|
|
|
-- This Week's work duration by user
|
|
SELECT
|
|
w1."user",
|
|
date_trunc('week', CURRENT_DATE) AS week_start,
|
|
SUM(
|
|
EXTRACT(EPOCH FROM (w2.ts - w1.ts))/3600
|
|
) AS duration_hours,
|
|
MIN(CASE WHEN w1.state = 'working' THEN w1.ts END) AS first_login_time
|
|
FROM
|
|
work_events w1
|
|
JOIN
|
|
work_events w2 ON w1."user" = w2."user"
|
|
AND w1.state = 'working'
|
|
AND w2.state = 'stopped'
|
|
AND w2.ts > w1.ts
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM work_events w3
|
|
WHERE w3."user" = w1."user"
|
|
AND w3.ts > w1.ts AND w3.ts < w2.ts
|
|
)
|
|
WHERE
|
|
date_trunc('week', w1.ts) = date_trunc('week', CURRENT_DATE)
|
|
GROUP BY
|
|
w1."user"
|
|
ORDER BY
|
|
w1."user";
|
|
|
|
-- This Month's work duration by user
|
|
SELECT
|
|
w1."user",
|
|
date_trunc('month', CURRENT_DATE) AS month_start,
|
|
SUM(
|
|
EXTRACT(EPOCH FROM (w2.ts - w1.ts))/3600
|
|
) AS duration_hours,
|
|
MIN(CASE WHEN w1.state = 'working' THEN w1.ts END) AS first_login_time
|
|
FROM
|
|
work_events w1
|
|
JOIN
|
|
work_events w2 ON w1."user" = w2."user"
|
|
AND w1.state = 'working'
|
|
AND w2.state = 'stopped'
|
|
AND w2.ts > w1.ts
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM work_events w3
|
|
WHERE w3."user" = w1."user"
|
|
AND w3.ts > w1.ts AND w3.ts < w2.ts
|
|
)
|
|
WHERE
|
|
date_trunc('month', w1.ts) = date_trunc('month', CURRENT_DATE)
|
|
GROUP BY
|
|
w1."user"
|
|
ORDER BY
|
|
w1."user"; |