work-tracing/README.md
2025-05-16 17:55:30 +04:00

427 lines
19 KiB
Markdown

# Employee Workstation Activity Tracking System
A system for logging user activity (login events, active/inactive periods) on Windows workstations and reporting these events to a central Flask server. The server provides a web dashboard for viewing daily, weekly, and monthly aggregated working time summaries.
## Installation Instructions
### Client-Side Setup (Windows Workstations)
**Prerequisites:**
* Windows 10 or Windows 11
* PowerShell 3.0 or higher
* Administrative privileges (for setting up the scheduled task)
**Steps:**
1. **Copy Script:** Transfer the `report.ps1` script to a suitable location on the client workstation (e.g., `C:\Scripts\UserActivityTracker\`).
* The `report.ps1` script and `schedule_task.ps1` can be found in the `client_tools/` directory of the project.
2. **Configure:**
* Create a `config.env` file in the same directory as `report.ps1` with the following content:
```env
API_ENDPOINT="http://your-server-address:5050/api/report"
IDLE_THRESHOLD_MINUTES="10"
POLL_INTERVAL_SECONDS="60"
REPORT_INTERVAL_MINUTES="1"
```
Replace `http://your-server-address:5050/api/report` with the actual URL of your server's reporting endpoint.
* Alternatively, these values can be set as system-wide environment variables on the workstation.
3. **Schedule Task:**
* Copy the `schedule_task.ps1` script (from the `client_tools/` directory) to the workstation.
* Open PowerShell as an Administrator.
* Navigate to the directory where you saved `schedule_task.ps1`.
* Run the script, providing the path to `report.ps1`:
```powershell
.\schedule_task.ps1 -ScriptPath "C:\Scripts\UserActivityTracker\report.ps1"
```
This will create a scheduled task that runs `report.ps1` when any user logs on.
### Server-Side Setup
**Prerequisites:**
* Python 3.9 or higher
* `pip` (Python package installer)
* A database system: SQLite (for development/testing) or PostgreSQL (recommended for production).
* Git (for cloning the repository)
**Steps:**
1. **Clone Repository:**
```bash
git clone <repository_url> # Replace <repository_url> with the actual URL
cd employee-workstation-activity-tracking # Or your project directory name
```
2. **Create Virtual Environment:**
```bash
python -m venv venv
```
3. **Activate Virtual Environment:**
* On Windows:
```cmd
.\venv\Scripts\activate
```
* On macOS/Linux:
```bash
source venv/bin/activate
```
4. **Install Dependencies:**
```bash
pip install -r requirements.txt
```
5. **Configure Application:**
* Create a `config.env` file in the project root directory (e.g., alongside `run.py`).
* Add the following necessary configurations:
```env
# Flask Secret Key (change this to a random string for production)
SECRET_KEY="your_very_secret_flask_key"
# Database URI
# For SQLite (creates a file in the 'instance' folder, good for development):
DATABASE_URI="sqlite:///../instance/work_events.db"
# For PostgreSQL (replace with your actual connection details):
# DATABASE_URI="postgresql://username:password@host:port/database_name"
# Optional: Define HOST, PORT, DEBUG for Flask development server
# HOST="0.0.0.0"
# PORT="5050"
# DEBUG="True"
```
**Important:**
* Replace `your_very_secret_flask_key` with a strong, random string for production.
* Choose and configure either SQLite or PostgreSQL for `DATABASE_URI`.
* The `instance` folder will be created automatically by the application if it doesn't exist.
6. **Initialize Database:**
* Ensure your `config.env` is correctly set up with the `DATABASE_URI`.
* The application is designed to create the database and all necessary tables (including `work_events` and `user_real_work_summary`) if they don't exist when it first runs. For explicit control, use the Flask CLI command:
```bash
flask init-db
```
This command should be run from the project root directory with the virtual environment activated. It ensures all tables defined in `app/models.py` are created in the database.
* Alternatively, if you need to set up or inspect the database schema manually (e.g., for PostgreSQL), SQL script files are provided in the `database_utils/` directory:
* `database_utils/create_db.sql` (original schema, may need review if used directly against current models)
* `database_utils/001_create_user_real_work_summary.sql` (for the real work hours summary table)
Example for PostgreSQL:
```bash
psql -U your_pg_user -d your_pg_database -f database_utils/your_script_file.sql
```
It's generally recommended to use `flask init-db` for consistency with the application's models.
## Usage Examples
### Running the Server
Ensure your virtual environment is activated before running the server.
Use the `start_app.sh` script located in the project root:
```bash
./start_app.sh [mode]
```
* **`[mode]`** is optional.
* If omitted or `dev` or `development`, the server starts in development mode (using `python run.py`).
* If `prod` or `production`, the server starts in production mode (using Gunicorn).
**Example for Development:**
```bash
./start_app.sh dev
# or simply
./start_app.sh
```
**Example for Production:**
```bash
./start_app.sh prod
```
The application will typically start on `http://0.0.0.0:5050/` or `http://127.0.0.1:5050/` by default. Check the terminal output for the exact address.
The script will attempt to activate the `venv` virtual environment.
### Accessing the Dashboard
Once the server is running, open a web browser and navigate to the server's address (e.g., `http://your-server-ip:5050/` or `http://localhost:5050/` if running locally).
The dashboard provides views for daily, weekly, and monthly user activity summaries. It now displays "Real Work Hours," which are calculated based on continuous 40-minute blocks of 'working' activity reported by the client.
### Client-Side Activity Reporting
The `report.ps1` script, once configured and scheduled on a Windows workstation, runs automatically at user logon. It monitors user idle time based on the `IDLE_THRESHOLD_MINUTES` setting (default 10 minutes) and polls at intervals defined by `POLL_INTERVAL_SECONDS` (default 60 seconds).
When a user's state changes (e.g., from active to idle/stopped, or idle to active/working), the script sends an HTTP POST request to the server's `/api/report` endpoint.
**Example API Payload Sent by Client:**
```json
{
"user": "domain\\username",
"state": "working",
"ts": "2024-05-10T10:20:30Z"
}
```
* `user`: The Windows username (often including domain).
* `state`: Either `"working"` or `"stopped"`.
* `ts`: An optional ISO 8601 UTC timestamp. If not provided by the client, the server records the event with its current UTC time.
### Manual API Interaction (for testing)
You can also send POST requests to the `/api/report` endpoint using tools like `curl` or Postman for testing purposes.
**Example using `curl` for raw event reporting:**
```bash
curl -X POST -H "Content-Type: application/json" \
-d '{"user":"testuser","state":"working","ts":"2024-01-01T12:00:00Z"}' \
http://localhost:5050/api/report
```
Successful requests will receive a JSON response like `{"success": true}` with an HTTP 201 status code.
**New API Endpoint for Real Work Hours Data:**
A new endpoint `/api/reports/real_work_hours` is available to fetch the calculated "Real Work Hours".
* **Method:** `GET`
* **Query Parameters:**
* `username` (string, optional): Filter results by a specific username.
* `start_date` (string, optional): Start date for the report period (YYYY-MM-DD).
* `end_date` (string, optional): End date for the report period (YYYY-MM-DD). If `start_date` is provided and `end_date` is omitted, `end_date` defaults to `start_date`.
* **Example `curl`:**
```bash
curl -X GET "http://localhost:5050/api/reports/real_work_hours?username=testuser&start_date=2024-01-01&end_date=2024-01-31"
```
* **Example Response:**
```json
{
"success": true,
"data": [
{
"id": 1,
"username": "testuser",
"work_date": "2024-01-15",
"real_hours_counted": 3,
"last_processed_event_id": 12345
},
// ... more records
]
}
```
## Project Structure
The project is organized as follows:
```text
work-tracing/
├── app/ # Main Flask application package
│ ├── __init__.py # Application factory (create_app), blueprint registration
│ ├── api/ # REST API Blueprints and modules
│ │ ├── __init__.py
│ │ ├── events.py # Endpoints for event reporting
│ │ └── reports.py # Endpoints for data retrieval
│ ├── models.py # SQLAlchemy database models (includes WorkEvent, UserRealWorkSummary)
│ ├── utils/ # Utility modules (query building, formatting)
│ │ ├── __init__.py
│ │ ├── formatting.py
│ │ └── queries.py
│ ├── views/ # Web page view Blueprints
│ │ ├── __init__.py
│ │ └── dashboard.py
│ ├── errors.py # Custom error handlers registration
│ ├── scheduler.py # APScheduler setup and job definitions
│ ├── services/ # Service layer for business logic (e.g., work_hours_service.py)
│ └── cli.py # Custom Flask CLI commands (e.g., process-real-hours)
├── client_tools/ # Scripts and utilities for client-side setup
│ ├── report.ps1 # PowerShell script for client activity reporting
│ ├── schedule_task.ps1 # PowerShell script to schedule client agent
│ └── run_hidden.vbs # VBScript to run PowerShell script hidden (optional)
├── database_utils/ # Database-related utility scripts
│ ├── create_db.sql # Manual SQL schema creation script (reference for work_events)
│ └── 001_create_user_real_work_summary.sql # Manual SQL for user_real_work_summary table
├── instance/ # Instance-specific data (SQLite DB, logs), .gitignored
├── static/ # Static files (CSS, JavaScript)
│ ├── css/
│ │ └── dashboard.css
│ └── js/ # JavaScript modules for the dashboard
│ ├── dashboard.js
│ └── ... (other .js files)
├── templates/ # HTML templates
│ └── dashboard.html
├── venv/ # Python virtual environment
├── .cursorignore
├── .flake8
├── .gitignore
├── config.env # Server-side environment configuration. IMPORTANT: .gitignore this if it contains secrets.
├── ecosystem.config.js # PM2 configuration (if used for deployment)
├── README.md # This file
├── requirements.txt # Python dependencies
├── run.py # Application entry point (used by start_app.sh for dev mode)
└── start_app.sh # Primary script to start the application (dev/prod)
```
## Time Synchronization
The application maintains consistent time handling across all components:
1. **Client-side (PowerShell):**
* All timestamps are generated in UTC using `(Get-Date).ToUniversalTime().ToString("o")`
* Timestamps are sent to the server in ISO 8601 format with 'Z' suffix indicating UTC
2. **Backend (Flask):**
* All incoming timestamps are converted to UTC if they have timezone information
* Backend code uses `datetime.utcnow()` for server-generated timestamps
* All time-based calculations are performed in UTC
3. **Database (PostgreSQL/SQLite):**
* For PostgreSQL, the database session timezone is set to 'Asia/Dubai' (UTC+4)
* Timestamps are automatically converted from UTC to Asia/Dubai when stored
* When retrieved, timestamps reflect the Asia/Dubai timezone
4. **Frontend (JavaScript):**
* All displayed times use GMT+4 (Asia/Dubai) via the `formatTimeToGMT4()` function
* Date selection for filtering is in the user's local timezone
* Auto-refresh interval (60 seconds) aligns with client reporting frequency
This approach ensures that all timestamps are consistently handled throughout the application, preventing time drift or synchronization issues between components.
## Dependencies and Requirements
### Client-Side Requirements (Windows Workstations)
* **Operating System:** Windows 10 or Windows 11
* **PowerShell:** Version 3.0 or higher
* **Permissions:** Administrative privileges are required to set up the scheduled task for the client script (`report.ps1`).
### Server-Side Requirements
* **Python:** Version 3.9 or higher.
* **Database:**
* SQLite 3 for development or small-scale deployments.
* PostgreSQL (version 12+ recommended) for production or larger deployments.
* **Networking:** Clients must be able to reach the server over HTTP on the configured port (default 5050) within the local area network (LAN).
* **Python Packages:** The specific Python dependencies are listed in `requirements.txt`. Key packages include:
* Flask (web framework)
* Flask-SQLAlchemy (SQLAlchemy integration for Flask)
* SQLAlchemy (SQL toolkit and Object Relational Mapper)
* Gunicorn (WSGI HTTP server for production on Unix-like systems)
* psycopg2-binary (PostgreSQL adapter for Python, if using PostgreSQL)
* python-dotenv (for managing environment variables from `.env` files)
* requests (for making HTTP requests, used internally by the server)
* APScheduler (for scheduling background tasks, like calculating real work hours)
**Full `requirements.txt`:**
```
Flask==2.3.3
Flask-SQLAlchemy==3.1.1
SQLAlchemy==2.0.20
gunicorn==21.2.0
python-dotenv==1.0.0
psycopg2-binary==2.9.7
pytest==7.4.0
black==23.7.0
flake8==6.1.0
isort==5.12.0
alembic==1.12.0
Werkzeug==2.3.7
Jinja2==3.1.2
itsdangerous==2.1.2
click==8.1.7
requests
APScheduler==3.10.4
```
Installation of these server-side dependencies is typically done by running `pip install -r requirements.txt` within an activated virtual environment.
## Automated Tasks and Manual Operations
### Real Work Hours Calculation
The system includes a new table, `user_real_work_summary`, which stores "Real Work Hours." A "Real Work Hour" is defined as a continuous block of 40 minutes where the user's status is reported as 'working' by the client-side script.
* **Automated Processing:**
* An automated background task, managed by `APScheduler` within the Flask application, runs periodically (e.g., every 15 minutes).
* This task (`calculate_and_store_real_work_hours` located in `app/services/work_hours_service.py`) processes new `work_events` from the `work_events` table.
* It identifies 40-minute 'working' blocks and updates the `real_hours_counted` and `last_processed_event_id` in the `user_real_work_summary` table for each user and date.
* **Manual Trigger / Backfilling:**
* A Flask CLI command is available for manual operations:
```bash
flask process-real-hours
```
* This command executes the same `calculate_and_store_real_work_hours` function.
* It is useful for initial data backfilling if there's a large history of `work_events` before this feature was added, for testing the calculation logic, or for ad-hoc processing if the scheduler was temporarily inactive.
## Contributing Guidelines
We welcome contributions to improve the Employee Workstation Activity Tracking System! To ensure a smooth process, please follow these guidelines.
### Getting Started
1. **Fork the repository** on GitHub (or your Git hosting platform).
2. **Clone your fork** locally: `git clone <your-fork-url>`
3. **Create a feature branch** for your changes: `git checkout -b feat/your-feature-name` or `fix/your-bug-fix`.
### Coding Standards
Consistency helps maintain code quality and readability.
* **Python:**
* Adhere to **PEP 8** style guidelines.
* Use **Black** for code formatting. It's recommended to configure your editor to format on save or run it before committing.
* Use **isort** for organizing imports.
* Use **Flake8** for linting to catch common errors and style issues. Configuration for Flake8 can be found in the `.flake8` file in the project root (e.g., `max-line-length = 88` to align with Black).
* **Before committing Python code, please run these tools from the project root directory:**
```bash
# Ensure your virtual environment (e.g., venv) is activated
black .
isort .
flake8 .
```
Address any critical issues reported by Flake8.
* **PowerShell:**
* Use Verb-Noun function and cmdlet names (e.g., `Get-UserIdleTime`, `Send-ActivityReport`).
* Maintain consistent two-space indentation.
* **JavaScript:**
* Use modern ES6+ syntax.
* Write modular code, leveraging ES6 modules.
* Avoid polluting the global scope.
### Commit Messages
Please follow the **Conventional Commits** specification for your commit messages. This helps in generating automated changelogs and makes the project history more understandable.
Examples:
* `feat: add detailed user activity log endpoint`
* `fix: correct calculation for weekly report aggregation`
* `docs: update installation instructions for PostgreSQL`
* `style: apply Black formatting to all Python files`
* `refactor: improve exception handling in API modules`
* `test: add unit tests for date utility functions`
* `chore: update Gunicorn version in requirements.txt`
### Testing
* If you add new features, please include tests where applicable. This project uses `pytest` for Python testing.
* Ensure all existing tests pass before submitting a pull request.
```bash
# To run tests (ensure pytest is installed and virtual env is active):
pytest
```
### Pull Requests
1. Once your changes are complete and tested, commit them with a clear Conventional Commit message.
2. Push your feature branch to your fork: `git push origin feat/your-feature-name`.
3. Open a **Pull Request (PR)** to the `main` branch of the original repository.
4. In your PR description, clearly explain the changes you've made, why they were made, and reference any related issues (e.g., "Closes #123").
5. Be prepared to discuss your changes and make adjustments if requested during the review process.
Thank you for contributing!
## License
This project is licensed under the MIT License. A copy of the license should ideally be included as a `LICENSE` file in the project's root directory.
If a `LICENSE` file is not present, the general terms of the MIT License can be found at: [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)