htmlgraph 0.9.3__py3-none-any.whl → 0.27.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/.htmlgraph/agents.json +72 -0
- htmlgraph/.htmlgraph/htmlgraph.db +0 -0
- htmlgraph/__init__.py +173 -17
- htmlgraph/__init__.pyi +123 -0
- htmlgraph/agent_detection.py +127 -0
- htmlgraph/agent_registry.py +45 -30
- htmlgraph/agents.py +160 -107
- htmlgraph/analytics/__init__.py +9 -2
- htmlgraph/analytics/cli.py +190 -51
- htmlgraph/analytics/cost_analyzer.py +391 -0
- htmlgraph/analytics/cost_monitor.py +664 -0
- htmlgraph/analytics/cost_reporter.py +675 -0
- htmlgraph/analytics/cross_session.py +617 -0
- htmlgraph/analytics/dependency.py +192 -100
- htmlgraph/analytics/pattern_learning.py +771 -0
- htmlgraph/analytics/session_graph.py +707 -0
- htmlgraph/analytics/strategic/__init__.py +80 -0
- htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
- htmlgraph/analytics/strategic/pattern_detector.py +876 -0
- htmlgraph/analytics/strategic/preference_manager.py +709 -0
- htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
- htmlgraph/analytics/work_type.py +190 -14
- htmlgraph/analytics_index.py +135 -51
- htmlgraph/api/__init__.py +3 -0
- htmlgraph/api/cost_alerts_websocket.py +416 -0
- htmlgraph/api/main.py +2498 -0
- htmlgraph/api/static/htmx.min.js +1 -0
- htmlgraph/api/static/style-redesign.css +1344 -0
- htmlgraph/api/static/style.css +1079 -0
- htmlgraph/api/templates/dashboard-redesign.html +1366 -0
- htmlgraph/api/templates/dashboard.html +794 -0
- htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
- htmlgraph/api/templates/partials/activity-feed.html +1100 -0
- htmlgraph/api/templates/partials/agents-redesign.html +317 -0
- htmlgraph/api/templates/partials/agents.html +317 -0
- htmlgraph/api/templates/partials/event-traces.html +373 -0
- htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
- htmlgraph/api/templates/partials/features.html +578 -0
- htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
- htmlgraph/api/templates/partials/metrics.html +346 -0
- htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
- htmlgraph/api/templates/partials/orchestration.html +198 -0
- htmlgraph/api/templates/partials/spawners.html +375 -0
- htmlgraph/api/templates/partials/work-items.html +613 -0
- htmlgraph/api/websocket.py +538 -0
- htmlgraph/archive/__init__.py +24 -0
- htmlgraph/archive/bloom.py +234 -0
- htmlgraph/archive/fts.py +297 -0
- htmlgraph/archive/manager.py +583 -0
- htmlgraph/archive/search.py +244 -0
- htmlgraph/atomic_ops.py +560 -0
- htmlgraph/attribute_index.py +208 -0
- htmlgraph/bounded_paths.py +539 -0
- htmlgraph/builders/__init__.py +14 -0
- htmlgraph/builders/base.py +118 -29
- htmlgraph/builders/bug.py +150 -0
- htmlgraph/builders/chore.py +119 -0
- htmlgraph/builders/epic.py +150 -0
- htmlgraph/builders/feature.py +31 -6
- htmlgraph/builders/insight.py +195 -0
- htmlgraph/builders/metric.py +217 -0
- htmlgraph/builders/pattern.py +202 -0
- htmlgraph/builders/phase.py +162 -0
- htmlgraph/builders/spike.py +52 -19
- htmlgraph/builders/track.py +148 -72
- htmlgraph/cigs/__init__.py +81 -0
- htmlgraph/cigs/autonomy.py +385 -0
- htmlgraph/cigs/cost.py +475 -0
- htmlgraph/cigs/messages_basic.py +472 -0
- htmlgraph/cigs/messaging.py +365 -0
- htmlgraph/cigs/models.py +771 -0
- htmlgraph/cigs/pattern_storage.py +427 -0
- htmlgraph/cigs/patterns.py +503 -0
- htmlgraph/cigs/posttool_analyzer.py +234 -0
- htmlgraph/cigs/reporter.py +818 -0
- htmlgraph/cigs/tracker.py +317 -0
- htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/cli/.htmlgraph/agents.json +72 -0
- htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
- htmlgraph/cli/__init__.py +42 -0
- htmlgraph/cli/__main__.py +6 -0
- htmlgraph/cli/analytics.py +1424 -0
- htmlgraph/cli/base.py +685 -0
- htmlgraph/cli/constants.py +206 -0
- htmlgraph/cli/core.py +954 -0
- htmlgraph/cli/main.py +147 -0
- htmlgraph/cli/models.py +475 -0
- htmlgraph/cli/templates/__init__.py +1 -0
- htmlgraph/cli/templates/cost_dashboard.py +399 -0
- htmlgraph/cli/work/__init__.py +239 -0
- htmlgraph/cli/work/browse.py +115 -0
- htmlgraph/cli/work/features.py +568 -0
- htmlgraph/cli/work/orchestration.py +676 -0
- htmlgraph/cli/work/report.py +728 -0
- htmlgraph/cli/work/sessions.py +466 -0
- htmlgraph/cli/work/snapshot.py +559 -0
- htmlgraph/cli/work/tracks.py +486 -0
- htmlgraph/cli_commands/__init__.py +1 -0
- htmlgraph/cli_commands/feature.py +195 -0
- htmlgraph/cli_framework.py +115 -0
- htmlgraph/collections/__init__.py +18 -0
- htmlgraph/collections/base.py +415 -98
- htmlgraph/collections/bug.py +53 -0
- htmlgraph/collections/chore.py +53 -0
- htmlgraph/collections/epic.py +53 -0
- htmlgraph/collections/feature.py +12 -26
- htmlgraph/collections/insight.py +100 -0
- htmlgraph/collections/metric.py +92 -0
- htmlgraph/collections/pattern.py +97 -0
- htmlgraph/collections/phase.py +53 -0
- htmlgraph/collections/session.py +194 -0
- htmlgraph/collections/spike.py +56 -16
- htmlgraph/collections/task_delegation.py +241 -0
- htmlgraph/collections/todo.py +511 -0
- htmlgraph/collections/traces.py +487 -0
- htmlgraph/config/cost_models.json +56 -0
- htmlgraph/config.py +190 -0
- htmlgraph/context_analytics.py +344 -0
- htmlgraph/converter.py +216 -28
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +2406 -307
- htmlgraph/dashboard.html.backup +6592 -0
- htmlgraph/dashboard.html.bak +7181 -0
- htmlgraph/dashboard.html.bak2 +7231 -0
- htmlgraph/dashboard.html.bak3 +7232 -0
- htmlgraph/db/__init__.py +38 -0
- htmlgraph/db/queries.py +790 -0
- htmlgraph/db/schema.py +1788 -0
- htmlgraph/decorators.py +317 -0
- htmlgraph/dependency_models.py +19 -2
- htmlgraph/deploy.py +142 -125
- htmlgraph/deployment_models.py +474 -0
- htmlgraph/docs/API_REFERENCE.md +841 -0
- htmlgraph/docs/HTTP_API.md +750 -0
- htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
- htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
- htmlgraph/docs/README.md +532 -0
- htmlgraph/docs/__init__.py +77 -0
- htmlgraph/docs/docs_version.py +55 -0
- htmlgraph/docs/metadata.py +93 -0
- htmlgraph/docs/migrations.py +232 -0
- htmlgraph/docs/template_engine.py +143 -0
- htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
- htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
- htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
- htmlgraph/docs/templates/base_agents.md.j2 +78 -0
- htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
- htmlgraph/docs/version_check.py +163 -0
- htmlgraph/edge_index.py +182 -27
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +100 -52
- htmlgraph/event_migration.py +13 -4
- htmlgraph/exceptions.py +49 -0
- htmlgraph/file_watcher.py +101 -28
- htmlgraph/find_api.py +75 -63
- htmlgraph/git_events.py +145 -63
- htmlgraph/graph.py +1122 -106
- htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/hooks/.htmlgraph/agents.json +72 -0
- htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
- htmlgraph/hooks/__init__.py +45 -0
- htmlgraph/hooks/bootstrap.py +169 -0
- htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
- htmlgraph/hooks/concurrent_sessions.py +208 -0
- htmlgraph/hooks/context.py +350 -0
- htmlgraph/hooks/drift_handler.py +525 -0
- htmlgraph/hooks/event_tracker.py +1314 -0
- htmlgraph/hooks/git_commands.py +175 -0
- htmlgraph/hooks/hooks-config.example.json +12 -0
- htmlgraph/hooks/installer.py +343 -0
- htmlgraph/hooks/orchestrator.py +674 -0
- htmlgraph/hooks/orchestrator_reflector.py +223 -0
- htmlgraph/hooks/post-checkout.sh +28 -0
- htmlgraph/hooks/post-commit.sh +24 -0
- htmlgraph/hooks/post-merge.sh +26 -0
- htmlgraph/hooks/post_tool_use_failure.py +273 -0
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/posttooluse.py +408 -0
- htmlgraph/hooks/pre-commit.sh +94 -0
- htmlgraph/hooks/pre-push.sh +28 -0
- htmlgraph/hooks/pretooluse.py +819 -0
- htmlgraph/hooks/prompt_analyzer.py +637 -0
- htmlgraph/hooks/session_handler.py +668 -0
- htmlgraph/hooks/session_summary.py +395 -0
- htmlgraph/hooks/state_manager.py +504 -0
- htmlgraph/hooks/subagent_detection.py +202 -0
- htmlgraph/hooks/subagent_stop.py +369 -0
- htmlgraph/hooks/task_enforcer.py +255 -0
- htmlgraph/hooks/task_validator.py +177 -0
- htmlgraph/hooks/validator.py +628 -0
- htmlgraph/ids.py +41 -27
- htmlgraph/index.d.ts +286 -0
- htmlgraph/learning.py +767 -0
- htmlgraph/mcp_server.py +69 -23
- htmlgraph/models.py +1586 -87
- htmlgraph/operations/README.md +62 -0
- htmlgraph/operations/__init__.py +79 -0
- htmlgraph/operations/analytics.py +339 -0
- htmlgraph/operations/bootstrap.py +289 -0
- htmlgraph/operations/events.py +244 -0
- htmlgraph/operations/fastapi_server.py +231 -0
- htmlgraph/operations/hooks.py +350 -0
- htmlgraph/operations/initialization.py +597 -0
- htmlgraph/operations/initialization.py.backup +228 -0
- htmlgraph/operations/server.py +303 -0
- htmlgraph/orchestration/__init__.py +58 -0
- htmlgraph/orchestration/claude_launcher.py +179 -0
- htmlgraph/orchestration/command_builder.py +72 -0
- htmlgraph/orchestration/headless_spawner.py +281 -0
- htmlgraph/orchestration/live_events.py +377 -0
- htmlgraph/orchestration/model_selection.py +327 -0
- htmlgraph/orchestration/plugin_manager.py +140 -0
- htmlgraph/orchestration/prompts.py +137 -0
- htmlgraph/orchestration/spawner_event_tracker.py +383 -0
- htmlgraph/orchestration/spawners/__init__.py +16 -0
- htmlgraph/orchestration/spawners/base.py +194 -0
- htmlgraph/orchestration/spawners/claude.py +173 -0
- htmlgraph/orchestration/spawners/codex.py +435 -0
- htmlgraph/orchestration/spawners/copilot.py +294 -0
- htmlgraph/orchestration/spawners/gemini.py +471 -0
- htmlgraph/orchestration/subprocess_runner.py +36 -0
- htmlgraph/orchestration/task_coordination.py +343 -0
- htmlgraph/orchestration.md +563 -0
- htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
- htmlgraph/orchestrator.py +669 -0
- htmlgraph/orchestrator_config.py +357 -0
- htmlgraph/orchestrator_mode.py +328 -0
- htmlgraph/orchestrator_validator.py +133 -0
- htmlgraph/parallel.py +646 -0
- htmlgraph/parser.py +160 -35
- htmlgraph/path_query.py +608 -0
- htmlgraph/pattern_matcher.py +636 -0
- htmlgraph/planning.py +147 -52
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/query_builder.py +109 -72
- htmlgraph/query_composer.py +509 -0
- htmlgraph/reflection.py +443 -0
- htmlgraph/refs.py +344 -0
- htmlgraph/repo_hash.py +512 -0
- htmlgraph/repositories/__init__.py +292 -0
- htmlgraph/repositories/analytics_repository.py +455 -0
- htmlgraph/repositories/analytics_repository_standard.py +628 -0
- htmlgraph/repositories/feature_repository.py +581 -0
- htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
- htmlgraph/repositories/feature_repository_memory.py +607 -0
- htmlgraph/repositories/feature_repository_sqlite.py +858 -0
- htmlgraph/repositories/filter_service.py +620 -0
- htmlgraph/repositories/filter_service_standard.py +445 -0
- htmlgraph/repositories/shared_cache.py +621 -0
- htmlgraph/repositories/shared_cache_memory.py +395 -0
- htmlgraph/repositories/track_repository.py +552 -0
- htmlgraph/repositories/track_repository_htmlfile.py +619 -0
- htmlgraph/repositories/track_repository_memory.py +508 -0
- htmlgraph/repositories/track_repository_sqlite.py +711 -0
- htmlgraph/routing.py +8 -19
- htmlgraph/scripts/deploy.py +1 -2
- htmlgraph/sdk/__init__.py +398 -0
- htmlgraph/sdk/__init__.pyi +14 -0
- htmlgraph/sdk/analytics/__init__.py +19 -0
- htmlgraph/sdk/analytics/engine.py +155 -0
- htmlgraph/sdk/analytics/helpers.py +178 -0
- htmlgraph/sdk/analytics/registry.py +109 -0
- htmlgraph/sdk/base.py +484 -0
- htmlgraph/sdk/constants.py +216 -0
- htmlgraph/sdk/core.pyi +308 -0
- htmlgraph/sdk/discovery.py +120 -0
- htmlgraph/sdk/help/__init__.py +12 -0
- htmlgraph/sdk/help/mixin.py +699 -0
- htmlgraph/sdk/mixins/__init__.py +15 -0
- htmlgraph/sdk/mixins/attribution.py +113 -0
- htmlgraph/sdk/mixins/mixin.py +410 -0
- htmlgraph/sdk/operations/__init__.py +12 -0
- htmlgraph/sdk/operations/mixin.py +427 -0
- htmlgraph/sdk/orchestration/__init__.py +17 -0
- htmlgraph/sdk/orchestration/coordinator.py +203 -0
- htmlgraph/sdk/orchestration/spawner.py +204 -0
- htmlgraph/sdk/planning/__init__.py +19 -0
- htmlgraph/sdk/planning/bottlenecks.py +93 -0
- htmlgraph/sdk/planning/mixin.py +211 -0
- htmlgraph/sdk/planning/parallel.py +186 -0
- htmlgraph/sdk/planning/queue.py +210 -0
- htmlgraph/sdk/planning/recommendations.py +87 -0
- htmlgraph/sdk/planning/smart_planning.py +319 -0
- htmlgraph/sdk/session/__init__.py +19 -0
- htmlgraph/sdk/session/continuity.py +57 -0
- htmlgraph/sdk/session/handoff.py +110 -0
- htmlgraph/sdk/session/info.py +309 -0
- htmlgraph/sdk/session/manager.py +103 -0
- htmlgraph/sdk/strategic/__init__.py +26 -0
- htmlgraph/sdk/strategic/mixin.py +563 -0
- htmlgraph/server.py +685 -180
- htmlgraph/services/__init__.py +10 -0
- htmlgraph/services/claiming.py +199 -0
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +1392 -175
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/session_warning.py +201 -0
- htmlgraph/sessions/__init__.py +23 -0
- htmlgraph/sessions/handoff.py +756 -0
- htmlgraph/setup.py +34 -17
- htmlgraph/spike_index.py +143 -0
- htmlgraph/sync_docs.py +12 -15
- htmlgraph/system_prompts.py +450 -0
- htmlgraph/templates/AGENTS.md.template +366 -0
- htmlgraph/templates/CLAUDE.md.template +97 -0
- htmlgraph/templates/GEMINI.md.template +87 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +146 -15
- htmlgraph/track_manager.py +69 -21
- htmlgraph/transcript.py +890 -0
- htmlgraph/transcript_analytics.py +699 -0
- htmlgraph/types.py +323 -0
- htmlgraph/validation.py +115 -0
- htmlgraph/watch.py +8 -5
- htmlgraph/work_type_utils.py +3 -2
- {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2406 -307
- htmlgraph-0.27.5.data/data/htmlgraph/templates/AGENTS.md.template +366 -0
- htmlgraph-0.27.5.data/data/htmlgraph/templates/CLAUDE.md.template +97 -0
- htmlgraph-0.27.5.data/data/htmlgraph/templates/GEMINI.md.template +87 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +97 -64
- htmlgraph-0.27.5.dist-info/RECORD +337 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
- htmlgraph/cli.py +0 -2688
- htmlgraph/sdk.py +0 -709
- htmlgraph-0.9.3.dist-info/RECORD +0 -61
- {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
htmlgraph/find_api.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
BeautifulSoup-style Find API for HtmlGraph.
|
|
3
5
|
|
|
@@ -22,9 +24,10 @@ Example:
|
|
|
22
24
|
nodes = graph.find_all(properties__effort__gt=8)
|
|
23
25
|
"""
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
|
|
26
28
|
import re
|
|
27
|
-
from
|
|
29
|
+
from collections.abc import Callable
|
|
30
|
+
from typing import TYPE_CHECKING, Any
|
|
28
31
|
|
|
29
32
|
if TYPE_CHECKING:
|
|
30
33
|
from htmlgraph.graph import HtmlGraph
|
|
@@ -56,14 +59,10 @@ class FindAPI:
|
|
|
56
59
|
nodes = graph.find_all(title__contains="auth")
|
|
57
60
|
"""
|
|
58
61
|
|
|
59
|
-
def __init__(self, graph:
|
|
62
|
+
def __init__(self, graph: HtmlGraph):
|
|
60
63
|
self._graph = graph
|
|
61
64
|
|
|
62
|
-
def find(
|
|
63
|
-
self,
|
|
64
|
-
type: str | None = None,
|
|
65
|
-
**kwargs
|
|
66
|
-
) -> 'Node | None':
|
|
65
|
+
def find(self, type: str | None = None, **kwargs: Any) -> Node | None:
|
|
67
66
|
"""
|
|
68
67
|
Find the first node matching the given criteria.
|
|
69
68
|
|
|
@@ -80,7 +79,7 @@ class FindAPI:
|
|
|
80
79
|
node = graph.find(properties__effort__gt=8)
|
|
81
80
|
"""
|
|
82
81
|
if type is not None:
|
|
83
|
-
kwargs[
|
|
82
|
+
kwargs["type"] = type
|
|
84
83
|
|
|
85
84
|
for node in self._graph:
|
|
86
85
|
if self._matches(node, kwargs):
|
|
@@ -88,11 +87,8 @@ class FindAPI:
|
|
|
88
87
|
return None
|
|
89
88
|
|
|
90
89
|
def find_all(
|
|
91
|
-
self,
|
|
92
|
-
|
|
93
|
-
limit: int | None = None,
|
|
94
|
-
**kwargs
|
|
95
|
-
) -> list['Node']:
|
|
90
|
+
self, type: str | None = None, limit: int | None = None, **kwargs: Any
|
|
91
|
+
) -> list[Node]:
|
|
96
92
|
"""
|
|
97
93
|
Find all nodes matching the given criteria.
|
|
98
94
|
|
|
@@ -109,7 +105,7 @@ class FindAPI:
|
|
|
109
105
|
nodes = graph.find_all(status__in=["todo", "blocked"], limit=10)
|
|
110
106
|
"""
|
|
111
107
|
if type is not None:
|
|
112
|
-
kwargs[
|
|
108
|
+
kwargs["type"] = type
|
|
113
109
|
|
|
114
110
|
results = []
|
|
115
111
|
for node in self._graph:
|
|
@@ -119,7 +115,7 @@ class FindAPI:
|
|
|
119
115
|
break
|
|
120
116
|
return results
|
|
121
117
|
|
|
122
|
-
def find_by_id(self, node_id: str) ->
|
|
118
|
+
def find_by_id(self, node_id: str) -> Node | None:
|
|
123
119
|
"""
|
|
124
120
|
Find node by exact ID.
|
|
125
121
|
|
|
@@ -131,7 +127,7 @@ class FindAPI:
|
|
|
131
127
|
"""
|
|
132
128
|
return self._graph.get(node_id)
|
|
133
129
|
|
|
134
|
-
def find_by_title(self, title: str, exact: bool = False) -> list[
|
|
130
|
+
def find_by_title(self, title: str, exact: bool = False) -> list[Node]:
|
|
135
131
|
"""
|
|
136
132
|
Find nodes by title.
|
|
137
133
|
|
|
@@ -148,11 +144,8 @@ class FindAPI:
|
|
|
148
144
|
return self.find_all(title__icontains=title)
|
|
149
145
|
|
|
150
146
|
def find_related(
|
|
151
|
-
self,
|
|
152
|
-
|
|
153
|
-
relationship: str | None = None,
|
|
154
|
-
direction: str = "outgoing"
|
|
155
|
-
) -> list['Node']:
|
|
147
|
+
self, node_id: str, relationship: str | None = None, direction: str = "outgoing"
|
|
148
|
+
) -> list[Node]:
|
|
156
149
|
"""
|
|
157
150
|
Find nodes related to a given node.
|
|
158
151
|
|
|
@@ -165,9 +158,10 @@ class FindAPI:
|
|
|
165
158
|
List of related nodes
|
|
166
159
|
"""
|
|
167
160
|
neighbor_ids = self._graph.get_neighbors(node_id, relationship, direction)
|
|
168
|
-
|
|
161
|
+
nodes = [self._graph.get(nid) for nid in neighbor_ids]
|
|
162
|
+
return [n for n in nodes if n is not None]
|
|
169
163
|
|
|
170
|
-
def find_blocking(self, node_id: str) -> list[
|
|
164
|
+
def find_blocking(self, node_id: str) -> list[Node]:
|
|
171
165
|
"""
|
|
172
166
|
Find nodes that are blocking the given node.
|
|
173
167
|
|
|
@@ -177,9 +171,11 @@ class FindAPI:
|
|
|
177
171
|
Returns:
|
|
178
172
|
List of blocking nodes
|
|
179
173
|
"""
|
|
180
|
-
return self.find_related(
|
|
174
|
+
return self.find_related(
|
|
175
|
+
node_id, relationship="blocked_by", direction="outgoing"
|
|
176
|
+
)
|
|
181
177
|
|
|
182
|
-
def find_blocked_by(self, node_id: str) -> list[
|
|
178
|
+
def find_blocked_by(self, node_id: str) -> list[Node]:
|
|
183
179
|
"""
|
|
184
180
|
Find nodes that are blocked by the given node.
|
|
185
181
|
|
|
@@ -191,9 +187,10 @@ class FindAPI:
|
|
|
191
187
|
"""
|
|
192
188
|
# Nodes that have blocked_by pointing to this node
|
|
193
189
|
incoming = self._graph.get_incoming_edges(node_id, "blocked_by")
|
|
194
|
-
|
|
190
|
+
nodes = [self._graph.get(ref.source_id) for ref in incoming]
|
|
191
|
+
return [n for n in nodes if n is not None]
|
|
195
192
|
|
|
196
|
-
def _matches(self, node:
|
|
193
|
+
def _matches(self, node: Node, filters: dict[str, Any]) -> bool:
|
|
197
194
|
"""
|
|
198
195
|
Check if a node matches all filters.
|
|
199
196
|
|
|
@@ -209,7 +206,7 @@ class FindAPI:
|
|
|
209
206
|
return False
|
|
210
207
|
return True
|
|
211
208
|
|
|
212
|
-
def _check_filter(self, node:
|
|
209
|
+
def _check_filter(self, node: Node, key: str, expected: Any) -> bool:
|
|
213
210
|
"""
|
|
214
211
|
Check a single filter against a node.
|
|
215
212
|
|
|
@@ -223,33 +220,34 @@ class FindAPI:
|
|
|
223
220
|
"""
|
|
224
221
|
# Parse lookup type from key
|
|
225
222
|
# Check if the last part is a lookup keyword
|
|
226
|
-
parts = key.split(
|
|
223
|
+
parts = key.split("__")
|
|
227
224
|
|
|
228
225
|
if len(parts) == 1:
|
|
229
226
|
# Simple attribute
|
|
230
227
|
attr_path = parts[0]
|
|
231
|
-
lookup =
|
|
228
|
+
lookup = "exact"
|
|
232
229
|
elif parts[-1] in self._lookups:
|
|
233
230
|
# Last part is a lookup
|
|
234
|
-
attr_path =
|
|
231
|
+
attr_path = "__".join(parts[:-1])
|
|
235
232
|
lookup = parts[-1]
|
|
236
233
|
else:
|
|
237
234
|
# All parts are attribute path, use exact match
|
|
238
235
|
attr_path = key
|
|
239
|
-
lookup =
|
|
236
|
+
lookup = "exact"
|
|
240
237
|
|
|
241
238
|
# Get actual value
|
|
242
239
|
actual = self._get_attr(node, attr_path)
|
|
243
240
|
|
|
244
241
|
# Apply lookup
|
|
245
242
|
lookup_fn = self._lookups.get(lookup, self._exact)
|
|
246
|
-
|
|
243
|
+
result: bool = lookup_fn(actual, expected)
|
|
244
|
+
return result
|
|
247
245
|
|
|
248
|
-
def _get_attr(self, node:
|
|
246
|
+
def _get_attr(self, node: Node, path: str) -> Any:
|
|
249
247
|
"""Get attribute value supporting nested access with double underscore."""
|
|
250
248
|
# Convert double underscore to dot notation for nested access
|
|
251
|
-
path = path.replace(
|
|
252
|
-
parts = path.split(
|
|
249
|
+
path = path.replace("__", ".")
|
|
250
|
+
parts = path.split(".")
|
|
253
251
|
current = node
|
|
254
252
|
|
|
255
253
|
for part in parts:
|
|
@@ -266,12 +264,14 @@ class FindAPI:
|
|
|
266
264
|
|
|
267
265
|
# Lookup functions
|
|
268
266
|
def _exact(self, actual: Any, expected: Any) -> bool:
|
|
269
|
-
|
|
267
|
+
result: bool = actual == expected
|
|
268
|
+
return result
|
|
270
269
|
|
|
271
270
|
def _iexact(self, actual: Any, expected: Any) -> bool:
|
|
272
271
|
if actual is None:
|
|
273
272
|
return expected is None
|
|
274
|
-
|
|
273
|
+
result: bool = str(actual).lower() == str(expected).lower()
|
|
274
|
+
return result
|
|
275
275
|
|
|
276
276
|
def _contains(self, actual: Any, expected: Any) -> bool:
|
|
277
277
|
if actual is None:
|
|
@@ -312,7 +312,11 @@ class FindAPI:
|
|
|
312
312
|
def _iregex(self, actual: Any, expected: Any) -> bool:
|
|
313
313
|
if actual is None:
|
|
314
314
|
return False
|
|
315
|
-
pattern =
|
|
315
|
+
pattern = (
|
|
316
|
+
expected
|
|
317
|
+
if isinstance(expected, re.Pattern)
|
|
318
|
+
else re.compile(expected, re.IGNORECASE)
|
|
319
|
+
)
|
|
316
320
|
return bool(pattern.search(str(actual)))
|
|
317
321
|
|
|
318
322
|
def _gt(self, actual: Any, expected: Any) -> bool:
|
|
@@ -342,38 +346,44 @@ class FindAPI:
|
|
|
342
346
|
if actual is None:
|
|
343
347
|
return False
|
|
344
348
|
try:
|
|
345
|
-
actual_num =
|
|
346
|
-
|
|
347
|
-
|
|
349
|
+
actual_num = (
|
|
350
|
+
float(actual) if not isinstance(actual, (int, float)) else actual
|
|
351
|
+
)
|
|
352
|
+
expected_num = (
|
|
353
|
+
float(expected) if not isinstance(expected, (int, float)) else expected
|
|
354
|
+
)
|
|
355
|
+
result: bool = op(actual_num, expected_num)
|
|
356
|
+
return result
|
|
348
357
|
except (ValueError, TypeError):
|
|
349
|
-
|
|
358
|
+
result2: bool = op(str(actual), str(expected))
|
|
359
|
+
return result2
|
|
350
360
|
|
|
351
361
|
@property
|
|
352
362
|
def _lookups(self) -> dict[str, Callable]:
|
|
353
363
|
"""Available lookup functions."""
|
|
354
364
|
return {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
365
|
+
"exact": self._exact,
|
|
366
|
+
"iexact": self._iexact,
|
|
367
|
+
"contains": self._contains,
|
|
368
|
+
"icontains": self._icontains,
|
|
369
|
+
"startswith": self._startswith,
|
|
370
|
+
"istartswith": self._istartswith,
|
|
371
|
+
"endswith": self._endswith,
|
|
372
|
+
"iendswith": self._iendswith,
|
|
373
|
+
"regex": self._regex,
|
|
374
|
+
"iregex": self._iregex,
|
|
375
|
+
"gt": self._gt,
|
|
376
|
+
"gte": self._gte,
|
|
377
|
+
"lt": self._lt,
|
|
378
|
+
"lte": self._lte,
|
|
379
|
+
"in": self._in,
|
|
380
|
+
"not_in": self._not_in,
|
|
381
|
+
"isnull": self._isnull,
|
|
372
382
|
}
|
|
373
383
|
|
|
374
384
|
|
|
375
385
|
# Convenience functions for direct import
|
|
376
|
-
def find(graph:
|
|
386
|
+
def find(graph: HtmlGraph, type: str | None = None, **kwargs: Any) -> Node | None:
|
|
377
387
|
"""
|
|
378
388
|
Find first node matching criteria.
|
|
379
389
|
|
|
@@ -388,7 +398,9 @@ def find(graph: 'HtmlGraph', type: str | None = None, **kwargs) -> 'Node | None'
|
|
|
388
398
|
return FindAPI(graph).find(type=type, **kwargs)
|
|
389
399
|
|
|
390
400
|
|
|
391
|
-
def find_all(
|
|
401
|
+
def find_all(
|
|
402
|
+
graph: HtmlGraph, type: str | None = None, limit: int | None = None, **kwargs: Any
|
|
403
|
+
) -> list[Node]:
|
|
392
404
|
"""
|
|
393
405
|
Find all nodes matching criteria.
|
|
394
406
|
|