Terminal-based timesheet reporting for Jortt with DuckDB backend
Project description
Terminal-based timesheet reporting for Jortt with DuckDB backend
A Python project that ingests data from the Jortt API into a local DuckDB database using dlt (data load tool) with declarative REST API configuration and a Textual UI.
Features
- Declarative REST API Configuration: Uses dlt's built-in REST API source with minimal custom code
- OAuth 2.0 Authentication: Secure authentication with the Jortt API using access tokens
- Multiple Resources: Ingests projects and project line items from Jortt API
- Local DuckDB Storage: Stores data in a local DuckDB database file
- Automatic Pagination: Built-in pagination handling by dlt
- Semantic Layer: Built-in semantic model with pre-aggregated tables for reporting
- Textual TUI: Rich terminal-based user interface with:
- Keyboard navigation for weeks and months
- Toggle between hours and euros metrics
Prerequisites
- Python 3.11 or higher
- uv package manager
- Jortt API credentials (OAuth client ID and secret)
Setup
1. Clone or navigate to the project directory
cd jortt-report
2. Install dependencies
The project uses uv for dependency management:
uv sync
3. Configure environment variables
Edit your .env file and add your credentials:
# Jortt API Configuration (OAuth Client Credentials)
JORTT_CLIENT_ID=your_client_id_here
JORTT_CLIENT_SECRET=your_client_secret_here
JORTT_SCOPES=invoices:read invoices:write
# Optional: manually provide access token (will be fetched automatically if not provided)
# JORTT_ACCESS_TOKEN=your_access_token_here
# Optional: customize database path (default: jortt.duckdb)
DATABASE_PATH=jortt.duckdb
Getting Jortt API Credentials
- Visit the Jortt Developer Portal
- Register your application to get your Client ID and Client Secret
- Add them to your
.envfile - The pipeline will automatically fetch access tokens using the Client Credentials Grant flow
- Common scopes:
invoices:read,invoices:write,customers:read,customers:write
Usage
Get an Access Token (Optional)
If you want to manually fetch an access token to test your credentials:
uv run python -m jortt_report.auth
This will display your access token and token details. However, the pipeline will automatically fetch tokens when needed.
Run the pipeline
uv run python -m jortt_report
The pipeline will:
- Automatically fetch an access token using your client credentials if needed
- Extract projects and project line items from the Jortt API with automatic pagination
- Load the data into a local DuckDB database file (
jortt.duckdbby default) - Store tables in the
rawschema
View Reports
After running the pipeline, you can view your timesheet data using the Textual TUI:
Textual TUI (Terminal-based)
Local development:
uv run jortt-report
Install globally with uvx (recommended):
uvx jortt-report
This launches a rich terminal UI with:
- Async pipeline execution: Press 'r' to refresh data without blocking the UI
- Weekly and monthly reports: Side-by-side timesheet views
- Keyboard navigation:
- Arrow keys (←/→) to navigate weeks
- Shift+Arrow keys to navigate months
- 'm' to toggle between hours and euros
- 'q' to quit
- Custom theme: Blue (#3399CC) color scheme
- Instant feedback: Notifications appear immediately for pipeline operations
Query the data
You can also query the data directly using DuckDB:
# Open the DuckDB CLI
duckdb jortt.duckdb
# Or use Python
python
>>> import duckdb
>>> conn = duckdb.connect('jortt.duckdb')
>>> conn.execute("SELECT * FROM raw.timesheet LIMIT 5").fetchdf()
>>> conn.execute("SELECT * FROM raw.timesheet_by_week LIMIT 5").fetchdf()
just
Alternatively, install just and use the just command runner.
Project structure
jortt-report/
├── jortt_report/
│ ├── __init__.py
│ ├── __main__.py # Main entry point - runs pipeline
│ ├── auth.py # OAuth authentication helper
│ ├── pipeline.py # DLT pipeline with REST API config
│ ├── datamart.py # Semantic layer & aggregation tables
│ └── tui.py # Textual terminal UI
├── tests/ # Test suite
├── .env # Environment variables
├── .gitignore
├── justfile # just command runner
├── pyproject.toml # Project configuration
└── README.md
How It Works
- OAuth Authentication: The pipeline uses your Client ID and Secret to obtain an access token via OAuth 2.0 Client Credentials Grant
- Declarative Configuration: The REST API source is configured using a simple dictionary structure with endpoints, authentication, and pagination settings
- Data Extraction: dlt's built-in REST API source handles all API calls and pagination automatically
- Data Loading: DLT loads the data into a local DuckDB file using the
replacewrite disposition - Semantic Layer: Creates a unified timesheet view and pre-aggregated tables using boring-semantic-layer
- Visualization: Textual TUI provides a terminal-based interface to query and navigate the optimized aggregation tables
Authentication Flow
The project supports two authentication methods:
-
Automatic (Recommended): Provide
JORTT_CLIENT_IDandJORTT_CLIENT_SECRETin your.envfile. The pipeline will automatically fetch access tokens as needed. -
Manual: Fetch an access token manually and provide it as
JORTT_ACCESS_TOKENin your.envfile.
Data Model
The pipeline creates the following tables in the local DuckDB file (in the raw schema):
Source Tables
- projects: Main table containing project data from the Jortt API
- project_line_items: Table containing project line item data
- customers: Customer information
- _dlt_loads: DLT metadata table tracking load operations
- _dlt_pipeline_state: DLT state management table
- _dlt_version: DLT version information
Views
- timesheet: Unified view joining projects and line items with customer info
Aggregation Tables
Pre-computed aggregations for fast reporting:
- timesheet_by_date: Daily aggregations per project (hours, value, count)
- timesheet_by_week: Weekly aggregations per project
- timesheet_by_month: Monthly aggregations per project
Additional nested tables may be created automatically by dlt for nested JSON structures (e.g., projects__customer_record__cc_emails).
All tables and views are accessible with the raw. prefix (e.g., raw.timesheet, raw.timesheet_by_week).
Extending the Pipeline
To add more resources from the Jortt API, simply add them to the resources list in the configuration in pipeline.py:
"resources": [
{
"name": "projects",
"endpoint": {
"path": "projects",
"params": {
"per_page": 100,
},
},
},
{
"name": "project_line_items",
"endpoint": {
"path": "project_line_items",
"params": {
"per_page": 100,
},
},
},
# Add new resources here
{
"name": "invoices",
"endpoint": {
"path": "invoices",
"params": {
"per_page": 100,
},
},
},
]
That's it! No custom Python code needed. The dlt REST API source handles everything automatically.
Resources
- Jortt API documentation
- DLT documentation
- DLT REST API source documentation
- DuckDB documentation
- Textual
- uv
License
GNU General Public License v3.0
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jortt_report-0.1.2.tar.gz.
File metadata
- Download URL: jortt_report-0.1.2.tar.gz
- Upload date:
- Size: 102.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d69622e0eda1019ff2874cc8973270541a99a004aa53fb14b0b117135d2d06b
|
|
| MD5 |
d63dbe6949fd82e5ee3c281d5734c627
|
|
| BLAKE2b-256 |
7e50a27f9d71434534fe6eb18bad69ebca58ce6d4fe1861a0124ec845237979e
|
File details
Details for the file jortt_report-0.1.2-py3-none-any.whl.
File metadata
- Download URL: jortt_report-0.1.2-py3-none-any.whl
- Upload date:
- Size: 27.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc5d72bc3205fd8902f4f87ba488c353dbc910bcf2141a673fbd5d488f1519b0
|
|
| MD5 |
a052eb97919116112f56acf5494b8348
|
|
| BLAKE2b-256 |
d5a6e7a7390f6a961b4c69a06fe3e31497d8b55df567e78b07708849234f9921
|