7.7 KiB
ZynkTime
A time reporting application for the Kleer API with both FastAPI web interface and CLI.
Features
- 🎨 Modern Web UI: Responsive glassy interface with drag/drop upload and live status
- 📑 CSV + XLSX: Parse CSV or XLSX (reads
AGRESSOsheet) for time extraction - ✅ Approvals: View and approve events over a date range from the UI or API
- 🌐 REST API: FastAPI endpoints for projects, reporting, events, and approvals
- 💻 CLI: Interactive command-line mode for CSV/XLSX files
- 🧪 Tested: Unit tests with pytest and mocked API calls
Quick Start
🐳 Docker (Recommended)
The easiest way to run ZynkTime is with Docker:
# 1. Clone the repository
cd zynktime
# 2. Copy environment file
cp .env.example .env
# 3. Edit .env and add your API key
nano .env # or use your favorite editor
# 4. Start with Docker Compose
docker-compose up -d
# 5. Open your browser
# Navigate to: http://localhost:8000
Docker Commands:
# Start the application
docker-compose up -d
# View logs
docker-compose logs -f
# Stop the application
docker-compose down
# Rebuild after code changes
docker-compose up -d --build
# Access container shell
docker-compose exec zynktime bash
🐍 Manual Installation
If you prefer to run without Docker:
# Clone the repository
cd zynktime
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
Configuration
Option 1: Environment File (Recommended)
# Copy example file
cp .env.example .env
# Edit with your values
nano .env
Option 2: Export Variables
export KLEER_API_KEY="your_api_key_here"
export KLEER_USERNAME="Your Name" # Optional, defaults to "Christopher Juhlin"
export KLEER_COMPANY_ID="1336" # Optional, defaults to "1336"
Running the Application
Web UI Mode (Recommended):
python main.py
# Open your browser and navigate to: http://localhost:8000
CLI Mode:
python main.py cli
# Interactive CLI for processing CSV files
Using the Web UI
- Open your browser to
http://localhost:8000 - Upload your CSV or XLSX (XLSX uses the
AGRESSOsheet) via drag/drop or browse - Select a project from the dropdown
- Click "Report Time" to submit
- Optionally load and approve events by selecting a date range in the Approve Events panel
- View the results summary with total hours and days reported
The web interface features:
- 🎯 Drag-and-drop file upload
- 📋 Real-time project list
- 💫 Smooth animations and transitions
- 📱 Fully responsive design
- 🔔 Toast notifications for feedback
- ✅ Connection status indicator
API Endpoints
GET /
Serves the modern web UI interface.
GET /api
Returns API information and available endpoints.
GET /health
Health check endpoint - verifies API key configuration.
Response:
{
"status": "healthy",
"api_key_configured": true
}
GET /projects
Lists all active projects for the configured user.
Response:
{
"Project Name": {
"id": 123,
"activity": {"id": 456}
}
}
POST /report-time
Upload a CSV or XLSX file and report time to a specific project.
Parameters:
project_name(query): Name of the projectfile(form): CSV or XLSX file with time entries (XLSX sheet:AGRESSO)
Response:
{
"message": "Time reported successfully.",
"reported_days": 5,
"total_hours": 40.0,
"project_name": "Project Name",
"user_name": "Your Name"
}
GET /docs
Interactive API documentation (Swagger UI).
GET /events
List events for the configured user within a date range.
Query parameters:
start_date(YYYY-MM-DD)end_date(YYYY-MM-DD)
POST /approve-events
Approve events for the configured user within a date range.
Query parameters:
start_date(YYYY-MM-DD)end_date(YYYY-MM-DD)
CSV/XLSX Format
The application automatically detects the CSV delimiter (comma or semicolon) and column structure. XLSX parsing reads rows from the AGRESSO worksheet.
Supported Formats
Format 1: Comma-separated with headers
reg_period,xreg_period,T,Time code,Time code (T),Vouch.date,Project Activity,Project Activity (T),Work package,Work package (T),Hours,Workflow status,Text
202544,,A,0,Standard Time,10/27/2025,,,,,2.00,N,
202544,,A,0,Standard Time,10/28/2025,,,,,4.00,N,
Format 2: Semicolon-separated
...;...;...;...;...;01/15/2024;...;...;...;...;8.5;...
...;...;...;...;...;01/16/2024;...;...;...;...;7,5;...
Column Detection
- Date column: Automatically detected by searching for columns containing "date" or "vouch" in the header
- Hours column: Automatically detected by searching for columns containing "hour" in the header
- Date formats: Supports both
MM/DD/YYYYandYYYY-MM-DD - Hours format: Supports decimal hours with comma (
,) or dot (.) as separator - Whitespace: Automatically trimmed from values
- XLSX: Only the
AGRESSOworksheet is read; empty rows are skipped and date cells are normalized
Development
Project Structure
zynktime/
├── static/
│ ├── index.html # Modern web UI
│ ├── css/
│ │ └── style.css # Responsive styling
│ └── js/
│ └── app.js # Frontend logic
├── main.py # FastAPI application entry point
├── cli.py # CLI interface
├── config.py # Configuration management
├── models.py # Pydantic models
├── api_client.py # Kleer API client
├── services.py # Business logic
├── test_main.py # Unit tests
├── requirements.txt # Python dependencies
├── AGENTS.md # Guidelines for AI agents
└── README.md # This file
Running Tests
# Run all tests
pytest
# Run with verbose output
pytest -v
# Run specific test
pytest test_main.py::TestServices::test_validate_date_valid -v
# Run with coverage
pytest --cov=. --cov-report=html
Code Quality
The codebase follows:
- Type hints: Full typing support
- Error handling: Specific exceptions with proper logging
- Separation of concerns: Modular architecture
- Documentation: Comprehensive docstrings
- Testing: Unit tests for core functionality
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
KLEER_API_KEY |
Yes | - | API key for Kleer API |
KLEER_USERNAME |
No | Christopher Juhlin | Username for API requests |
KLEER_COMPANY_ID |
No | 1336 | Company ID for API requests |
Docker Deployment
Production Deployment
For production, you can customize the docker-compose.yml:
version: '3.8'
services:
zynktime:
image: zynktime:latest
restart: always
ports:
- "80:8000" # Expose on port 80
environment:
- KLEER_API_KEY=${KLEER_API_KEY}
- KLEER_USERNAME=${KLEER_USERNAME}
- KLEER_COMPANY_ID=${KLEER_COMPANY_ID}
env_file:
- .env
Behind a Reverse Proxy
If deploying behind nginx or traefik:
# docker-compose.yml
services:
zynktime:
image: zynktime:latest
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.zynktime.rule=Host(`time.example.com`)"
Health Checks
The Docker container includes health checks:
- Endpoint:
/health - Interval: 30 seconds
- Timeout: 10 seconds
- Retries: 3
Monitor with:
docker-compose ps
docker inspect zynktime | grep Health
License
This project is for internal use with the Kleer API.