|
| 1 | +import logging |
| 2 | +from datetime import datetime, timezone |
| 3 | +from typing import TYPE_CHECKING, Any, Dict, Literal, Union |
| 4 | + |
| 5 | +from writer.core import Config |
| 6 | +from writer.keyvalue_storage import writer_kv_storage |
| 7 | + |
| 8 | +if TYPE_CHECKING: |
| 9 | + from writer.blueprints import Graph, GraphNode |
| 10 | + from writer.core_ui import Component |
| 11 | + |
| 12 | + |
| 13 | +logger = logging.getLogger("journal") |
| 14 | + |
| 15 | + |
| 16 | +class JournalRecord: |
| 17 | + def __init__( |
| 18 | + self, |
| 19 | + execution_environment: Dict, |
| 20 | + title: str, |
| 21 | + graph: "Graph" |
| 22 | + ): |
| 23 | + self.started_at = datetime.now(timezone.utc) |
| 24 | + self.instance_type = "editor" if Config.mode == "edit" else "agent" |
| 25 | + |
| 26 | + self.trigger = { |
| 27 | + "event": execution_environment.get("context", {}).get("event"), |
| 28 | + "component": {} |
| 29 | + } |
| 30 | + |
| 31 | + component: "Union[GraphNode, Component]" |
| 32 | + if self.trigger["event"] == "wf-run-blueprint": |
| 33 | + component = graph.nodes[0].component |
| 34 | + self.trigger["component"]["type"] = "blueprint" |
| 35 | + self.trigger["component"]["id"] = graph.nodes[0].component.parentId |
| 36 | + else: |
| 37 | + component = graph.get_start_nodes()[0] |
| 38 | + self.trigger["component"]["type"] = "block" |
| 39 | + self.trigger["component"]["id"] = graph.get_start_nodes()[0].id |
| 40 | + |
| 41 | + if "API" in title: |
| 42 | + if getattr(component, "type", "") == "blueprints_crontrigger": |
| 43 | + self.trigger["type"] = "Cron" |
| 44 | + else: |
| 45 | + self.trigger["type"] = "API" |
| 46 | + elif "UI" in title: |
| 47 | + self.trigger["type"] = "UI" |
| 48 | + else: |
| 49 | + self.trigger["type"] = "On demand" |
| 50 | + |
| 51 | + self.graph = graph |
| 52 | + |
| 53 | + def to_dict(self) -> Dict[str, Any]: |
| 54 | + block_outputs = {} |
| 55 | + for graph_node in self.graph.nodes: |
| 56 | + block_outputs[graph_node.id] = {"result": graph_node.result, "outcome": graph_node.outcome} |
| 57 | + return { |
| 58 | + "timestamp": self.started_at.isoformat(), |
| 59 | + "instanceType": self.instance_type, |
| 60 | + "trigger": self.trigger, |
| 61 | + "blockOutputs": block_outputs, |
| 62 | + } |
| 63 | + |
| 64 | + def construct_key(self) -> str: |
| 65 | + return f"wf-journal-{self.instance_type[0]}-{int(self.started_at.timestamp() * 1000)}" |
| 66 | + |
| 67 | + def save(self, result: Literal["success", "error", "stopped"]) -> None: |
| 68 | + if "journal" not in Config.feature_flags or not writer_kv_storage.is_accessible(): |
| 69 | + return |
| 70 | + data = self.to_dict() |
| 71 | + data["result"] = result |
| 72 | + writer_kv_storage.save(self.construct_key(), data) |
0 commit comments