Skip to content

Commit 692a875

Browse files
feat(cognos): five components for IBM Cognos Analytics orchestration
Ships the same shape as tm1 / qlik_replicate / qlik_compose / jde: - cognos_resource — shared Cognos REST auth (session-based with security namespace: LDAP / CognosEx / etc.) - cognos_report_run_job — trigger a report run with parameter overrides; supports 6 output formats (PDF/HTML/CSV/XLSX/XML/JSON) with optional wait_for_completion polling - cognos_report_status_sensor — poll last-run status; trigger Dagster job on COMPLETED / SUCCEEDED / FAILED transitions; cursor-deduped - cognos_report_data_ingestion — run a report in CSV or JSON format and materialize the result as a DataFrame (foundation for Cognos → warehouse / dbt pipelines) - cognos_workspace — StateBackedComponent that enumerates reports in the specified folders; per-report selector (Fivetran shape); materializing an asset runs the underlying report Cognos Analytics is the BI/reports side of the IBM analytics stack; TM1 (planning cubes) is a separate integration. Bumps version to 0.10.34.
1 parent dbd2868 commit 692a875

33 files changed

Lines changed: 1096 additions & 2 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# CognosReportDataIngestionComponent
2+
3+
Run a **Cognos report** and materialize its output as a Dagster DataFrame. Right for landing report data in a warehouse or chaining Dagster transforms.
4+
5+
Pairs with `cognos_resource`.
6+
7+
## Output formats
8+
9+
Only CSV and JSON are parseable to DataFrames. Use `cognos_report_run_job` for PDF / HTML / XLSX exports (renders to file, not DataFrame).
10+
11+
## Example
12+
13+
```yaml
14+
type: dagster_community_components.CognosReportDataIngestionComponent
15+
attributes:
16+
asset_key: monthly_pnl_report
17+
report_id: i8B1A56A56789ABCDEF01234567890AB
18+
output_format: CSV
19+
parameters:
20+
Month: "2026-07"
21+
resource_key: cognos_resource
22+
```
23+
24+
## Chaining
25+
26+
- `dataframe_to_snowflake` — land Cognos data in a warehouse
27+
- `filter` + `summarize` — Cognos → analytics
28+
- `dataframe_to_csv` — export for spreadsheet users
29+
30+
## Related
31+
32+
- `cognos_resource`
33+
- `cognos_report_run_job`
34+
- `cognos_workspace`
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .component import CognosReportDataIngestionComponent
2+
3+
__all__ = ["CognosReportDataIngestionComponent"]
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""Cognos Report Data Ingestion Component.
2+
3+
Run a Cognos report with output_format=CSV or JSON, parse the result set
4+
as a DataFrame. Useful for landing report data into a warehouse or
5+
chaining downstream Dagster transforms.
6+
"""
7+
import io
8+
from typing import Any, Dict, Optional
9+
10+
import dagster as dg
11+
from pydantic import Field
12+
13+
14+
class CognosReportDataIngestionComponent(dg.Component, dg.Model, dg.Resolvable):
15+
"""Materialize a Cognos report's output as a DataFrame.
16+
17+
Example:
18+
19+
```yaml
20+
type: dagster_community_components.CognosReportDataIngestionComponent
21+
attributes:
22+
asset_key: monthly_pnl_report
23+
report_id: i8B1A56A56789ABCDEF01234567890AB
24+
output_format: CSV
25+
parameters:
26+
Month: "2026-07"
27+
resource_key: cognos_resource
28+
```
29+
"""
30+
31+
asset_key: str = Field(description="Asset key for the emitted DataFrame.")
32+
report_id: str = Field(description="Cognos report ID.")
33+
output_format: str = Field(default="CSV", description="CSV | JSON — parseable formats.")
34+
parameters: Optional[Dict[str, Any]] = Field(default=None)
35+
timeout_seconds: int = Field(default=600)
36+
group_name: Optional[str] = Field(default=None)
37+
resource_key: str = Field(default="cognos_resource")
38+
39+
def build_defs(self, context: dg.ComponentLoadContext) -> dg.Definitions:
40+
_self = self
41+
required_resource_keys = {self.resource_key}
42+
43+
@dg.asset(
44+
name=_self.asset_key,
45+
group_name=_self.group_name,
46+
required_resource_keys=required_resource_keys,
47+
compute_kind="cognos",
48+
)
49+
def _the_asset(context: dg.AssetExecutionContext):
50+
try:
51+
import pandas as pd
52+
import requests
53+
except ImportError as e:
54+
raise Exception("pandas or requests library not installed") from e
55+
56+
resource = getattr(context.resources, _self.resource_key)
57+
session = requests.Session()
58+
session.verify = resource.verify_ssl
59+
headers = resource.get_auth_headers()
60+
61+
login_body = resource.login_body()
62+
if login_body is not None:
63+
r = session.post(f"{resource.api_base}/session", json=login_body, timeout=30)
64+
if r.status_code >= 300:
65+
raise Exception(f"Cognos login failed: {r.status_code}")
66+
67+
fmt = _self.output_format.upper()
68+
if fmt not in ("CSV", "JSON"):
69+
raise Exception(f"Only CSV or JSON output_format supported for ingestion; got {fmt}")
70+
71+
body: dict = {"format": fmt}
72+
if _self.parameters:
73+
body["parameters"] = [{"name": k, "value": str(v)} for k, v in _self.parameters.items()]
74+
75+
run_url = f"{resource.report_url(_self.report_id)}/data"
76+
r = session.post(run_url, json=body, headers=headers, timeout=_self.timeout_seconds)
77+
if r.status_code >= 400:
78+
raise Exception(f"Cognos report run failed: {r.status_code} {r.text[:200]}")
79+
80+
if fmt == "CSV":
81+
df = pd.read_csv(io.StringIO(r.text))
82+
else: # JSON
83+
payload = r.json() or {}
84+
rows = payload.get("data") or payload.get("rows") or payload
85+
if isinstance(rows, dict):
86+
# Nested — walk one level to find a list.
87+
for v in rows.values():
88+
if isinstance(v, list):
89+
rows = v
90+
break
91+
df = pd.DataFrame(rows if isinstance(rows, list) else [])
92+
93+
context.add_output_metadata({
94+
"row_count": len(df),
95+
"report_id": _self.report_id,
96+
"output_format": fmt,
97+
})
98+
return df
99+
100+
return dg.Definitions(assets=[_the_asset])
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
type: dagster_community_components.CognosReportDataIngestionComponent
2+
attributes:
3+
asset_key: monthly_pnl_report
4+
report_id: i8B1A56A56789ABCDEF01234567890AB
5+
output_format: CSV # CSV | JSON
6+
parameters:
7+
Month: "2026-07"
8+
resource_key: cognos_resource
9+
group_name: cognos_finance
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pandas>=2.0.0
2+
requests>=2.28.0
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"component_type": "dagster_community_components.CognosReportDataIngestionComponent",
3+
"description": "Run a Cognos report in CSV or JSON output format and materialize the result as a Dagster DataFrame. Foundation for Cognos → warehouse / dbt pipelines. Parameter overrides supported.",
4+
"category": "ingestion",
5+
"icon": "TableChart",
6+
"attributes": {
7+
"asset_key": {"type": "string", "label": "Asset Key", "required": true},
8+
"report_id": {"type": "string", "label": "Report ID", "required": true, "description": "Cognos Store ID for the report."},
9+
"output_format": {"type": "string", "label": "Output Format", "required": false, "default": "CSV", "description": "Report output format. Only CSV and JSON are parseable to DataFrames.", "enum": ["CSV", "JSON"], "ui:widget": "select"},
10+
"parameters": {"type": "object", "label": "Report Parameters", "required": false},
11+
"timeout_seconds": {"type": "integer", "label": "Timeout (s)", "required": false, "default": 600},
12+
"group_name": {"type": "string", "label": "Group Name", "required": false},
13+
"resource_key": {"type": "string", "label": "Resource Key", "required": false, "default": "cognos_resource"}
14+
}
15+
}

dagster_community_components/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,12 @@
10251025
"JDEOrchestrationStatusSensorComponent": "sensors/jde_orchestration_status_sensor/component.py",
10261026
"JDEOrchestrationOutputIngestionComponent": "assets/ingestion/jde_orchestration_output_ingestion/component.py",
10271027
"JDEOrchestratorWorkspaceComponent": "integrations/jde_orchestrator_workspace/component.py",
1028+
"CognosResource": "resources/cognos_resource/component.py",
1029+
"CognosResourceComponent": "resources/cognos_resource/component.py",
1030+
"CognosReportRunJobComponent": "jobs/cognos_report_run_job/component.py",
1031+
"CognosReportStatusSensorComponent": "sensors/cognos_report_status_sensor/component.py",
1032+
"CognosReportDataIngestionComponent": "assets/ingestion/cognos_report_data_ingestion/component.py",
1033+
"CognosWorkspaceComponent": "integrations/cognos_workspace/component.py",
10281034
}
10291035

10301036
_loaded: dict[str, object] = {}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# CognosWorkspaceComponent
2+
3+
Auto-emit one Dagster asset per **Cognos Analytics report** by enumerating reports in the specified folders via the Cognos REST API. `StateBackedComponent` — discovery cached to disk. Materializing an asset runs the underlying report.
4+
5+
The workspace-shape peer of the low-level `cognos_*` components.
6+
7+
## Example
8+
9+
```yaml
10+
type: dagster_community_components.CognosWorkspaceComponent
11+
attributes:
12+
base_url_env_var: COGNOS_URL
13+
username_env_var: COGNOS_USER
14+
password_env_var: COGNOS_PASSWORD
15+
namespace_env_var: COGNOS_NAMESPACE
16+
report_selector:
17+
by_pattern: ["Monthly*", "Daily*"]
18+
exclude_by_pattern: ["*_deprecated"]
19+
group_name: cognos_reports
20+
output_format: CSV
21+
defs_state:
22+
management_type: LOCAL_FILESYSTEM
23+
refresh_if_dev: true
24+
```
25+
26+
## Selector
27+
28+
Same Fivetran-shape as other vendor workspaces (`by_name` / `by_pattern` / `exclude_by_name` / `exclude_by_pattern`).
29+
30+
## Folder walking
31+
32+
`folder_ids` accepts Cognos searchPath-style strings — e.g. `/content/folder[@name='Finance']`. Omit for root (`/`). If you have thousands of reports, narrow to specific folders to keep discovery fast.
33+
34+
## Related
35+
36+
- `cognos_resource`
37+
- `cognos_report_run_job`
38+
- `cognos_report_status_sensor`
39+
- `cognos_report_data_ingestion` — same execution, but parses CSV/JSON into a DataFrame
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .component import CognosReportSelector, CognosWorkspaceComponent
2+
3+
__all__ = ["CognosReportSelector", "CognosWorkspaceComponent"]

0 commit comments

Comments
 (0)