Source code for snowloader.loaders.changes

"""Change request loader for snowloader.

Fetches change requests from the ServiceNow change_request table and formats
them into structured documents. The content layout emphasizes the change type,
risk level, implementation window (start/end dates), and assignment details
that are most relevant for LLM-based change analysis and advisory use cases.

Author: Roni Das
"""

from __future__ import annotations

import logging
from typing import Any

from snowloader.loaders._field_utils import display_value as _display_value
from snowloader.loaders._field_utils import raw_value as _raw_value
from snowloader.models import BaseSnowLoader, SnowDocument

logger = logging.getLogger(__name__)


[docs] class ChangeLoader(BaseSnowLoader): """Loads change request records from ServiceNow. Produces documents that capture the full change lifecycle: what is being changed, the risk assessment, the implementation schedule, who is responsible, and which CI is affected. Optionally includes journal entries for CAB notes, implementation updates, and post-change reviews. Args: connection: An initialized SnowConnection instance. query: Optional encoded query for filtering change requests. fields: Optional field list override. include_journals: If True, fetches work notes and comments. Example: conn = SnowConnection(...) loader = ChangeLoader(conn, query="state=3") # Implement state for doc in loader.lazy_load(): print(doc.page_content[:200]) """ table = "change_request" content_fields = ["short_description", "description"] def _record_to_document(self, record: dict[str, Any]) -> SnowDocument: """Build a change request document from a raw API record. Args: record: Raw change_request record dict from the API. Returns: SnowDocument with formatted change request content and metadata. """ number = _display_value(record.get("number")) summary = _display_value(record.get("short_description")) description = _display_value(record.get("description")) change_type = _display_value(record.get("type")) state = _display_value(record.get("state")) priority = _display_value(record.get("priority")) risk = _display_value(record.get("risk")) category = _display_value(record.get("category")) assigned_to = _display_value(record.get("assigned_to")) assignment_group = _display_value(record.get("assignment_group")) cmdb_ci = _display_value(record.get("cmdb_ci")) start_date = _display_value(record.get("start_date")) end_date = _display_value(record.get("end_date")) opened_at = _display_value(record.get("opened_at")) closed_at = _display_value(record.get("closed_at")) lines = [ f"Change Request: {number}", f"Summary: {summary}", ] if description: lines.append(f"Description: {description}") lines.append(f"Type: {change_type}") lines.append(f"State: {state}") lines.append(f"Priority: {priority}") lines.append(f"Risk: {risk}") if category: lines.append(f"Category: {category}") if assigned_to: lines.append(f"Assigned To: {assigned_to}") if assignment_group: lines.append(f"Assignment Group: {assignment_group}") if cmdb_ci: lines.append(f"Configuration Item: {cmdb_ci}") # Implementation window is critical for change management if start_date: lines.append(f"Scheduled Start: {start_date}") if end_date: lines.append(f"Scheduled End: {end_date}") if opened_at: lines.append(f"Opened: {opened_at}") if closed_at: lines.append(f"Closed: {closed_at}") page_content = "\n".join(lines) sys_id = str(record.get("sys_id", "")) if self._include_journals and sys_id: journals = self._fetch_journals(sys_id) journal_text = self._format_journals(journals) if journal_text: page_content = page_content + "\n\n" + journal_text metadata: dict[str, Any] = { "sys_id": sys_id, "number": number, "table": self.table, "source": f"servicenow://change_request/{number}", "type": change_type, "state": state, "priority": priority, "risk": risk, "category": category, "assigned_to": assigned_to, "cmdb_ci": _raw_value(record.get("cmdb_ci")), "start_date": start_date, "end_date": end_date, "sys_created_on": _display_value(record.get("sys_created_on")), "sys_updated_on": _display_value(record.get("sys_updated_on")), } return SnowDocument(page_content=page_content, metadata=metadata)