Skip to main content

Exporting Manifest Job History to Excel with Python

This article walks through job_history_export.py, a command-line script that pulls completed job records from the Taqtile Manifest API and writes them to a formatted Excel spreadsheet. It also explains how you can adapt the script to build other reports against the same API.

What the Script Does

At a high level, the script:
  1. Authenticates with the Manifest REST API to get a JWT token
  2. Pages through all completed jobs via GraphQL
  3. Pages through all meter evidence records via GraphQL
  4. Filters both datasets in memory by optional username and/or date range
  5. Writes each evidence record as a row in an .xlsx file with bold column headers and auto-sized columns
The output filename is timestamped automatically (e.g. job_history_20250325_143012.xlsx).
job_history_export.py file:

Requirements

  • Python 3.7+
  • requests — HTTP client for the REST auth call and GraphQL queries
  • openpyxl — reads and writes .xlsx files without needing Excel installed

Usage

All three flags are optional and can be combined freely. Dates must be in YYYY-MM-DD format.

How It Works, Step by Step

1. Authentication

The script POSTs credentials to the Manifest REST endpoint:
The response JSON contains user.token, which is a JWT used as the Authorization header on every subsequent GraphQL request.

2. Fetching Completed Jobs (Paginated)

All completed jobs are fetched with a single GraphQL query, paginated in batches of 100:
The API returns a extensions.allPages field that tells the script the total number of pages. The loop continues until all pages have been fetched.
Note on dates: completionDate and startDate are Unix timestamps in milliseconds, returned as strings. The script converts these with datetime.fromtimestamp(int(ms) / 1000).

3. Fetching Meter Evidence (Paginated)

A second paginated query fetches all meter evidence records from metersEvidenceReport. These are indexed into a dictionary keyed by jobStepId so they can be joined with the steps from Step 2:
This avoids a separate API call per step — the full evidence dataset is loaded once and looked up by key.

4. Client-Side Filtering

Because the API doesn’t support server-side filtering by user or date range on the jobs query, filtering happens in Python after all data is fetched:
  • Username filter — compares assignedUserDetails.email against --username
  • Date range filter — converts completionDate milliseconds to a datetime.date and compares against --start-date / --end-date
Jobs that don’t pass both checks are discarded before writing.

5. Writing the Excel File

The script uses openpyxl to create a workbook with a single sheet called Job History. The columns are: There is one row per evidence record. Job and step fields repeat across rows that belong to the same step. After writing, the script auto-sizes each column (capped at 60 characters wide) using the longest value in that column.

Adapting the Script for Other Reports

The script is structured in clearly separated phases — auth, query, filter, write — making it straightforward to swap in different queries or output shapes.

Change the GraphQL Query

The queries are plain multi-line strings assigned to variables (JOB_QUERY, METER_EVIDENCE_QUERY). To report on a different resource, replace the query string and update the data extraction path:

Add or Remove Columns

The columns list defines the header row. Add, remove, or reorder entries there, then match the same changes to the ws.append(...) call that writes each data row. Keeping both in sync is the only constraint.

Add Server-Side Filters

Some Manifest queries accept filter arguments directly. If the query you’re using supports them, pass them as GraphQL variables rather than filtering in Python — this reduces the amount of data transferred:

Write Multiple Sheets

openpyxl supports multiple sheets in a single workbook. Create additional sheets before saving:

Change the Output Format

Swap openpyxl for the csv module to write a flat CSV instead:

Point at a Different Environment

The BASE_URL constant at the top of the script controls the target environment. Change it to switch between staging and production:

Security Note

The script currently has credentials hardcoded as string literals. Before sharing or deploying the script, move them to environment variables:
Then set them in your shell before running:

Summary

job_history_export.py demonstrates the typical pattern for building Manifest reports:
  1. Authenticate once via REST to get a token
  2. Page through the relevant GraphQL queries until all data is fetched
  3. Join or filter the data in Python
  4. Write the result to Excel with openpyxl
Because each phase is independent, swapping in a different query or output format requires changes in only one or two places, making it a good starting template for any new report you want to build against the Manifest API.