mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
fix(provenance): resolve merge conflicts, fix session API, move schemas
- Resolve all merge conflict markers in provenance.py, app.py, .gitignore - Revert broken session.get_nodes()/get_edges() to session.graph.nodes/edges - Keep undirected=True ego_graph fix for upstream ancestor traversal - Add direction field to ProvenanceEdge (upstream/downstream/lateral) - Group lineage edges in _render_markdown by direction section - Move ProvenanceNode/ProvenanceEdge/ProvenanceResponse to schemas.py - Restore complete router import set in app.py (sparql, vocabulary, etc.) Co-authored-by: Sameer6305 <sameer6305@users.noreply.github.com> Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
This commit is contained in:
co-authored by
Sameer6305
KaifAhmad1
parent
bc683e7a34
commit
7e815920ac
+1
-3
@@ -111,11 +111,9 @@ sample_data/
|
||||
# Test Results
|
||||
test_results.txt
|
||||
|
||||
<<<<<<< HEAD
|
||||
# Frontend workspace artifacts
|
||||
semantica-explorer/
|
||||
node_modules/
|
||||
=======
|
||||
|
||||
# Frontend build artifacts (generated by Vite — do not track in git)
|
||||
semantica/static/
|
||||
>>>>>>> upstream/main
|
||||
|
||||
@@ -92,16 +92,11 @@ def create_app(session: Optional[GraphSession] = None) -> FastAPI:
|
||||
from .routes.decisions import router as decisions_router
|
||||
from .routes.enrich import router as enrich_router
|
||||
from .routes.export_import import router as export_import_router
|
||||
<<<<<<< HEAD
|
||||
from .routes.annotations import router as annotations_router
|
||||
from .routes.provenance import router as provenance_router
|
||||
=======
|
||||
from .routes.graph import router as graph_router
|
||||
from .routes.provenance import router as provenance_router
|
||||
from .routes.sparql import router as sparql_router
|
||||
from .routes.temporal import router as temporal_router
|
||||
from .routes.vocabulary import router as vocabulary_router
|
||||
>>>>>>> upstream/main
|
||||
|
||||
app.include_router(graph_router)
|
||||
app.include_router(analytics_router)
|
||||
@@ -110,13 +105,9 @@ def create_app(session: Optional[GraphSession] = None) -> FastAPI:
|
||||
app.include_router(enrich_router)
|
||||
app.include_router(export_import_router)
|
||||
app.include_router(annotations_router)
|
||||
<<<<<<< HEAD
|
||||
app.include_router(provenance_router)
|
||||
=======
|
||||
app.include_router(sparql_router)
|
||||
app.include_router(provenance_router)
|
||||
app.include_router(vocabulary_router)
|
||||
>>>>>>> upstream/main
|
||||
|
||||
_WS_MAX_MESSAGE_BYTES = 64 * 1024 # 64 KB — control messages only
|
||||
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
"""
|
||||
=======
|
||||
"""
|
||||
>>>>>>> upstream/main
|
||||
Provenance routes for lineage visualization and exportable reports.
|
||||
"""
|
||||
|
||||
@@ -13,37 +9,13 @@ from typing import Any, Dict, List, Optional
|
||||
import networkx as nx
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from fastapi.responses import PlainTextResponse, Response
|
||||
from pydantic import BaseModel
|
||||
|
||||
from ..dependencies import get_session
|
||||
from ..schemas import ProvenanceEdge, ProvenanceNode, ProvenanceResponse
|
||||
from ..session import GraphSession
|
||||
|
||||
router = APIRouter(prefix="/api/provenance", tags=["Power User Tools"])
|
||||
|
||||
|
||||
class ProvenanceNode(BaseModel):
|
||||
id: str
|
||||
label: str
|
||||
prov_type: str
|
||||
parent_id: str
|
||||
|
||||
|
||||
class ProvenanceEdge(BaseModel):
|
||||
id: str
|
||||
source: str
|
||||
target: str
|
||||
label: str
|
||||
<<<<<<< HEAD
|
||||
direction: str # "upstream" | "downstream" | "lateral"
|
||||
=======
|
||||
>>>>>>> upstream/main
|
||||
|
||||
|
||||
class ProvenanceResponse(BaseModel):
|
||||
nodes: List[ProvenanceNode]
|
||||
edges: List[ProvenanceEdge]
|
||||
|
||||
|
||||
_AGENT_TYPES = {"person", "organization", "system", "agent"}
|
||||
_ACTIVITY_TYPES = {"action", "event", "process", "activity", "decision", "publication"}
|
||||
|
||||
@@ -61,32 +33,24 @@ def _build_provenance(session: GraphSession, node_id: Optional[str] = None) -> d
|
||||
if not node_id or node_id not in session.graph.nodes:
|
||||
return {"nodes": [], "edges": []}
|
||||
|
||||
graph = nx.DiGraph()
|
||||
nodes = session.get_nodes()
|
||||
edges = session.get_edges()
|
||||
nodes_by_id = {node.id: node for node in nodes}
|
||||
|
||||
if not node_id or node_id not in nodes_by_id:
|
||||
return {"nodes": [], "edges": []}
|
||||
|
||||
graph = nx.DiGraph()
|
||||
graph.add_node(node_id)
|
||||
|
||||
hop_nodes = {node_id}
|
||||
for edge in edges:
|
||||
for edge in session.graph.edges:
|
||||
if edge.source_id == node_id or edge.target_id == node_id:
|
||||
graph.add_edge(edge.source_id, edge.target_id, label=edge.edge_type)
|
||||
hop_nodes.add(edge.source_id)
|
||||
hop_nodes.add(edge.target_id)
|
||||
|
||||
for edge in edges:
|
||||
for edge in session.graph.edges:
|
||||
if edge.source_id in hop_nodes or edge.target_id in hop_nodes:
|
||||
graph.add_edge(edge.source_id, edge.target_id, label=edge.edge_type)
|
||||
|
||||
subgraph = nx.ego_graph(graph, node_id, radius=2, undirected=True)
|
||||
provenance_nodes: List[Dict[str, Any]] = []
|
||||
for graph_node_id in subgraph.nodes():
|
||||
node = nodes_by_id.get(graph_node_id)
|
||||
node = session.graph.nodes.get(graph_node_id)
|
||||
if node is None:
|
||||
continue
|
||||
prov_type, parent_id = _classify_prov(node.node_type)
|
||||
@@ -101,26 +65,19 @@ def _build_provenance(session: GraphSession, node_id: Optional[str] = None) -> d
|
||||
|
||||
provenance_edges: List[Dict[str, Any]] = []
|
||||
for source, target, data in subgraph.edges(data=True):
|
||||
<<<<<<< HEAD
|
||||
if target == node_id:
|
||||
direction = "upstream"
|
||||
elif source == node_id:
|
||||
direction = "downstream"
|
||||
else:
|
||||
direction = "lateral"
|
||||
|
||||
=======
|
||||
>>>>>>> upstream/main
|
||||
provenance_edges.append(
|
||||
{
|
||||
"id": f"{source}-{target}",
|
||||
"source": source,
|
||||
"target": target,
|
||||
"label": data.get("label", "related_to"),
|
||||
<<<<<<< HEAD
|
||||
"direction": direction,
|
||||
=======
|
||||
>>>>>>> upstream/main
|
||||
}
|
||||
)
|
||||
|
||||
@@ -159,39 +116,29 @@ def _render_markdown(report: Dict[str, Any]) -> str:
|
||||
for node in report.get("lineage", {}).get("nodes", []):
|
||||
lines.append(f"- `{node['id']}` ({node['prov_type']}): {node['label']}")
|
||||
|
||||
<<<<<<< HEAD
|
||||
edges = report.get("lineage", {}).get("edges", [])
|
||||
grouped_edges = {
|
||||
"upstream": [],
|
||||
"downstream": [],
|
||||
"lateral": [],
|
||||
}
|
||||
grouped_edges: Dict[str, List] = {"upstream": [], "downstream": [], "lateral": []}
|
||||
for edge in edges:
|
||||
direction = edge.get("direction", "lateral")
|
||||
if direction not in grouped_edges:
|
||||
direction = "lateral"
|
||||
grouped_edges[direction].append(edge)
|
||||
|
||||
if grouped_edges.get("upstream"):
|
||||
if grouped_edges["upstream"]:
|
||||
lines.extend(["", "## Upstream"])
|
||||
for edge in grouped_edges["upstream"]:
|
||||
lines.append(f"- `{edge['source']}` -[{edge['label']}]-> `{edge['target']}`")
|
||||
|
||||
if grouped_edges.get("downstream"):
|
||||
if grouped_edges["downstream"]:
|
||||
lines.extend(["", "## Downstream"])
|
||||
for edge in grouped_edges["downstream"]:
|
||||
lines.append(f"- `{edge['source']}` -[{edge['label']}]-> `{edge['target']}`")
|
||||
|
||||
if grouped_edges.get("lateral"):
|
||||
if grouped_edges["lateral"]:
|
||||
lines.extend(["", "## Lateral"])
|
||||
for edge in grouped_edges["lateral"]:
|
||||
lines.append(f"- `{edge['source']}` -[{edge['label']}]-> `{edge['target']}`")
|
||||
|
||||
=======
|
||||
lines.extend(["", "## Lineage Edges"])
|
||||
for edge in report.get("lineage", {}).get("edges", []):
|
||||
lines.append(f"- `{edge['source']}` -[{edge['label']}]-> `{edge['target']}`")
|
||||
>>>>>>> upstream/main
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
|
||||
@@ -288,3 +288,23 @@ class MergeResponse(BaseModel):
|
||||
merged_into: str
|
||||
removed_ids: List[str]
|
||||
edges_updated: int
|
||||
|
||||
|
||||
class ProvenanceNode(BaseModel):
|
||||
id: str
|
||||
label: str
|
||||
prov_type: str
|
||||
parent_id: Optional[str] = None
|
||||
|
||||
|
||||
class ProvenanceEdge(BaseModel):
|
||||
id: str
|
||||
source: str
|
||||
target: str
|
||||
label: str
|
||||
direction: str
|
||||
|
||||
|
||||
class ProvenanceResponse(BaseModel):
|
||||
nodes: List[ProvenanceNode]
|
||||
edges: List[ProvenanceEdge]
|
||||
|
||||
Reference in New Issue
Block a user