This tool calculates compensation for on-call shifts based on data from OpsGenie API or CSV files, following specific compensation rules for different time periods and special cases.
- Calculate on-call compensation with standard rates (5.56 € per hour outside working hours)
- Apply special rates for weekend short shifts (< 5 hours: 27.80 €) and night shifts (< 2 hours: 11.12 €)
- Consider individual working hours, time zones, and holidays
- Handle daylight saving time transitions
- Generate detailed reports and compensation summaries
- Create visualizations of daily compensation amounts
- 24.12. + 31.12 are considered 1/2 a workday, unless the downloaded Calendar data suggest a holiday.
- Creates an XSLX File if requested
--export-excel - Has a separate module for check for 24x7 compliance:
uv run ./check_oncall_rules.py shifts.csv - Custom Holidays per user (in user_profiles.json) are supported:
"country_code": "AT",
"custom_holidays": [
"2024-06-16",
"2024-10-31",
"2024-12-13"
]- Make sure you have Python 3.12 or later installed
- Clone this repository
- Install dependencies:
git clone https://github.com/pyveci/minuto.git
cd minuto
uv venv --python 3.12 --seed .venv
source .venv/bin/activate
uv pip install -e .
uv pip install buildYou can configure the tool using environment variables in a .env file:
# OpsGenie API configuration
OPSGENIE_API_TOKEN=your-api-token-here # OPSGENIE_API_KEY also accepted
OPSGENIE_SCHEDULE_ID=your-schedule-id-here
OPSGENIE_START_DATE=2025-01-01
OPSGENIE_END_DATE=2025-05-09
# File paths
OPSGENIE_CSV_FILE=shifts.csv
OPSGENIE_USER_PROFILES=user_profiles.json
OPSGENIE_OUTPUT_PLOT=compensation_chart.png
OPSGENIE_SAVE_CSV=shifts.csv
User profiles are stored in a JSON file and contain information about:
- User's email
- Time zone
- Working days (0-6, where 0 is Monday)
- Working hours
- Country and region for holiday calculation
- Custom holidays
The tool provides these main commands:
Retrieve on-call shift data directly from the OpsGenie API:
uv run main.py opsgenie --api-token YOUR_TOKEN --schedule-id YOUR_SCHEDULE_ID \
--start-date 2025-01-01 --end-date 2025-05-09 \
--save-csv shifts.csv --user-profiles user_profiles.jsonOr using environment variables:
# After setting up your .env file
uv run main.py opsgenieAtlassian migrated OpsGenie to JSM Operations. The jsm subcommand reads the same on-call data from the new backend. Schedule UUIDs carry over 1:1, so an existing OPSGENIE_SCHEDULE_ID works as the JSM schedule ID without any change.
uv run main.py jsm \
--cloud-id YOUR_CLOUD_ID --site-host your-org.atlassian.net \
--email you@example.com --api-token ATATT... \
--schedule-id YOUR_SCHEDULE_ID \
--start-date 2025-11-01 --end-date 2026-04-30 \
--save-csv shifts.csvOr using environment variables:
JSM_CLOUD_ID=... # Atlassian tenant UUID
JSM_SITE_HOST=your-org.atlassian.net
JSM_API_TOKEN_EMAIL=you@example.com
JSM_API_TOKEN=ATATT... # https://id.atlassian.com/manage-profile/security/api-tokens
JSM_SCHEDULE_ID=... # falls back to OPSGENIE_SCHEDULE_ID if unset
# Optional — JSM-specific envvars; each falls back to OPSGENIE_<NAME> if unset
JSM_SAVE_CSV=shifts.csv
JSM_USER_PROFILES=user_profiles.json
JSM_OUTPUT_PLOT=compensation_chart.png
JSM_EXPORT_EXCEL=shifts.xlsx
JSM_HISTORICAL_ONLY=1 # equivalent to --historical-only
uv run main.py jsm --start-date 2025-11-01 --end-date 2026-04-30Notes:
- By default, all timeline periods overlapping the window are imported (matching the
opsgeniecommand's behavior). Pass--historical-onlyto skipactiveandforecastperiods — recommended for mid-period payroll runs so unserved shifts don't get paid. - JSM responses identify users by Atlassian account ID, not email. The script resolves them once per run via the Jira
/rest/api/3/userendpoint. If any account can't be resolved (e.g. because the API token lacks the privacy scope or the user has hidden their email), the command exits non-zero and lists every unresolved ID at once — no placeholders end up in compensation reports.
If you already have on-call data in a CSV file:
uv run main.py csv shifts.csv --user-profiles user_profiles.json --output-plot compensation_chart.pngGenerate default user profiles from existing on-call data:
uv run main.py profiles shifts.csv --output user_profiles.jsonDownload holiday calendars for specific countries:
uv run main.py calendars --country AT --country FR --country ES --country BGYou can also download all supported calendars at once:
uv run main.py calendars --all- Fetch data from OpsGenie and save to CSV:
uv run main.py opsgenie \
--api-token YOUR_TOKEN \
--schedule-id YOUR_SCHEDULE_ID \
--start-date 2025-01-01 \
--end-date 2025-05-09 \
--save-csv shifts.csv- Generate user profiles:
uv run main.py profiles shifts.csv --output user_profiles.json-
Edit the user profiles to adjust timezones, working hours, etc.
-
Calculate compensation with the updated profiles:
uv run main.py csv shifts.csv --user-profiles user_profiles.json --output-plot chart.png- Create a
.envfile with your configuration:
OPSGENIE_API_TOKEN=your-token # OPSGENIE_API_KEY also accepted
OPSGENIE_SCHEDULE_ID=your-schedule-id
OPSGENIE_START_DATE=2025-01-01
OPSGENIE_END_DATE=2025-05-09
OPSGENIE_SAVE_CSV=shifts.csv
- Fetch data and save to CSV:
uv run main.py opsgenie- Create and adjust user profiles:
uv run main.py profiles shifts.csv
# Edit user_profiles.json to adjust settings- Run the compensation calculation:
uv run main.py csv shifts.csvThe tool generates a detailed compensation report showing:
=== DAILY COMPENSATION SUMMARY ===
User StartDay StartDate Start EndDate End Hours Amount (€) Pauschale
...
=== USER TOTALS ===
User Total Hours Total Amount (€)
...
=== GRAND TOTAL ===
Total compensation amount: XXX.XX €
The CSV file should have the following columns:
start: Start time of the shift (ISO 8601 format)end: End time of the shift (ISO 8601 format)hours: Duration of the shift in hoursuser: Email of the user who was on call