# 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) 