Project Management Leadership with AIAI-enabled project management with governance · Lesson 20 of 21

Lab: AI-assisted status reporting from Jira or Asana data

Article · 25 min · 8 min lecture

Video lecture

Lab: AI-assisted status reporting from Jira or Asana data

9 chapters · about 8 min · full transcript

Coming soon

Chapter 1 of 9

From tool data to a reviewed status report

  • Pull work items from Jira or Asana
  • Compute metrics you can defend
  • Draft with an approved AI tool; verify and publish

The narrated lecture is in production

Every chapter is scripted and ready. Browse the chapters and read the full transcript now — the video will appear here when it’s published.

Chapters

What you will build

A weekly pipeline: pull work items from Jira (REST API) or Asana (CSV export) → compute a small set of metrics in code → draft the narrative with an approved AI tool from the verified table only → verify every number and cause, add judgement, publish and record the AI use. Numbers always come from code, never from the model.

Step 1a: pull issues from Jira Cloud

Jira Cloud's issue search uses GET /rest/api/3/search/jql with token-based pagination (nextPageToken, isLast); the older /rest/api/3/search endpoint was removed in 2025. Check Atlassian's current REST documentation if a call fails.

import csv
import os
import requests

SITE = os.environ["JIRA_SITE"]            # e.g. https://your-company.atlassian.net
AUTH = (os.environ["JIRA_EMAIL"], os.environ["JIRA_API_TOKEN"])
JQL = 'project = CRM AND updated >= -90d ORDER BY created ASC'
FIELDS = "summary,status,assignee,duedate,created,resolutiondate,priority,labels"

rows, token = [], None
while True:
    params = {"jql": JQL, "fields": FIELDS, "maxResults": 100}
    if token:
        params["nextPageToken"] = token
    r = requests.get(f"{SITE}/rest/api/3/search/jql", params=params, auth=AUTH, timeout=30)
    r.raise_for_status()
    data = r.json()
    for it in data.get("issues", []):
        f = it["fields"]
        rows.append({
            "key": it["key"], "summary": f.get("summary"),
            "status_category": (f.get("status") or {}).get("statusCategory", {}).get("key"),
            "assignee": (f.get("assignee") or {}).get("displayName"),
            "due": f.get("duedate"), "created": f.get("created"),
            "resolved": f.get("resolutiondate"),
            "priority": (f.get("priority") or {}).get("name"),
            "labels": ";".join(f.get("labels") or []),
        })
    token = data.get("nextPageToken")
    if data.get("isLast", True) or not token:
        break

with open("work_items.csv", "w", newline="", encoding="utf-8") as fh:
    w = csv.DictWriter(fh, fieldnames=list(rows[0]) if rows else ["key"])
    w.writeheader()
    w.writerows(rows)
print(f"{len(rows)} issues written")

Create an API token in your Atlassian account settings and store it in an environment variable or a secrets manager. The summary field may contain personal or confidential text: it is used only locally and is not sent to the AI tool.

Step 1b: or export from Asana

In Asana, open the project and use the export option to download CSV. Map its columns (for example Task ID, Name, Assignee, Due Date, Created At, Completed At, Section/Column) to the same names as above: key, assignee, due, created, resolved, labels.

Step 2: compute defensible metrics

import pandas as pd

df = pd.read_csv("work_items.csv", parse_dates=["created", "resolved", "due"])
for col in ("created", "resolved", "due"):
    df[col] = pd.to_datetime(df[col], utc=True, errors="coerce")
now = pd.Timestamp.now(tz="UTC")
done = df.dropna(subset=["resolved"])
weekly = done.set_index("resolved").resample("W")["key"].count().tail(6)
open_items = df[df["resolved"].isna()]
remaining = len(open_items)
fast, slow = max(weekly.max(), 1), max(weekly.min(), 1)
status = {
    "data_date": now.date().isoformat(),
    "completed_this_week": int(weekly.iloc[-1]) if len(weekly) else 0,
    "throughput_last_6_weeks": ", ".join(str(int(x)) for x in weekly),
    "open_items": remaining,
    "overdue": int((open_items["due"] < now).sum()),
    "no_owner": int(open_items["assignee"].isna().sum()),
    "no_due_date": int(open_items["due"].isna().sum()),
    "forecast_weeks_range": f"{-(-remaining // fast)} to {-(-remaining // slow)}",
    "top_risks": "; ".join(open_items[open_items["labels"].fillna("").str.contains("risk")]
                            .sort_values("priority").head(3)["key"]),
}
pd.Series(status).to_csv("status_table.csv", header=["value"])
print(pd.Series(status))

The status table holds only aggregates and issue keys, so it is safe to share with an approved AI tool under most policies (check yours).

Step 3: the drafting prompt (any approved enterprise assistant)

ROLE: You support the project manager of the CRM rollout. You draft; the PM verifies and decides.
DATA: The status table below is the ONLY source. Data date is in the table.
WRITE TWO VERSIONS:
A) Sponsor (≤ 120 words): headline; forecast range; the one decision needed; top risk.
B) Team (≤ 250 words): progress; overdue and ownerless items; forecast; risks; actions with owners if stated.
RULES: After every number, cite the column in [brackets]. Do not calculate new numbers or round them.
If a cause is not in the table, write "cause to be confirmed". British English. No dramatic adjectives.
TABLE:
<paste status_table.csv>

Optional Step 3 via API (only if your organisation has approved API use)

import os
import anthropic

client = anthropic.Anthropic()                     # reads ANTHROPIC_API_KEY from the environment
table = open("status_table.csv", encoding="utf-8").read()
prompt = open("status_prompt.txt", encoding="utf-8").read().replace("<paste status_table.csv>", table)
try:
    msg = client.messages.create(
        model=os.environ.get("ANTHROPIC_MODEL", "claude-opus-5"),   # check current model names in the docs
        max_tokens=2000,
        system="You draft project status reports from verified tables. Never invent numbers or causes.",
        messages=[{"role": "user", "content": prompt}],
    )
    if msg.stop_reason == "refusal":
        raise SystemExit("Request declined; review the input.")
    print("".join(b.text for b in msg.content if b.type == "text"))
except anthropic.RateLimitError:
    print("Rate limited: retry later.")
except anthropic.APIStatusError as e:
    print(f"API error {e.status_code}: {e.message}")
except anthropic.APIConnectionError:
    print("Network problem: check connectivity.")

Install with pip install anthropic. Other approved assistants (for example Microsoft 365 Copilot or ChatGPT Enterprise) can run the same prompt through their own interfaces.

Step 4: verify, publish, record

[ ] Every bracketed number matches status_table.csv (no rounding, no new numbers)
[ ] Every cause confirmed with its owner, or marked "to be confirmed"
[ ] The one decision needed is stated with who and by when (your judgement, not the model's)
[ ] Brackets removed; AI-assistance note added per policy
[ ] AI use register updated (tool, data, tier: medium, reviewer)

How to measure success

  • Time per weekly report (including review) versus the baseline.
  • Corrections per report, by type (number, cause, tone), falling over time.
  • Ownerless and undated items in the tool falling week by week.

Key takeaways

  • Compute metrics in code from the tool's data; let the AI draft words only from a verified table.
  • Pull Jira Cloud data with the /rest/api/3/search/jql endpoint and token-based pagination, or use an Asana CSV export.
  • Keep API tokens in environment variables and send only aggregated, non-personal data to approved AI tools.
  • Require a citation for every number and "cause to be confirmed" when the table has no cause.
  • Verify, add judgement, publish and record the AI use in the register.

Check your understanding

Quick questions to lock in the lesson. They don’t count towards your certificate.

  1. Why does the lab compute metrics in Python rather than asking the AI to calculate them?
  2. A draft says "delays caused by the vendor", but the status table contains no cause. What should the PM do?
  3. Which data is most appropriate to paste into an approved AI tool for the status draft?

Put it into practice

Run the pipeline once on your own project: pull or export data, compute the status table, draft with an approved tool, and log every correction you make in review.

Enrol for free to save your progress

Reading is always free. Enrol to keep your place, take the final assessment and earn a verifiable certificate.