2025-05-16 17:55:30 +04:00

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";