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
Metadata
Release files for jortt-report 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| jortt_report-0.1.3.tar.gz | 102.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jortt_report-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 130.3 kB
Release files / jortt_report-0.1.3.tar.gz
| Download URL | jortt_report-0.1.3.tar.gz |
|---|---|
| Size | 102.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
943f5c07c4865d8da49fade282ebc899735ee283341f46e0131891e3d834415a
|
|
BLAKE2b-256 checksum How to use checksums |
c2efdf405945f190077265830ba274efbe76128800cca696a76ffcc9a6ccf67e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Release files / jortt_report-0.1.3-py3-none-any.whl
| Download URL | jortt_report-0.1.3-py3-none-any.whl |
|---|---|
| Size | 27.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8a26dde97c176486f81056d9036d12daa8dbb0348812f4cc5db55711aaca67a2
|
|
BLAKE2b-256 checksum How to use checksums |
782603bb1b147e7bfc14ad80aa471e3524ee4e2d63b90c8c2065d7e8daa0da6c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|