naas-abi 1.0.0__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.
- naas_abi/__init__.py +35 -0
- naas_abi/agents/AbiAgent.py +442 -0
- naas_abi/agents/AbiAgent_test.py +157 -0
- naas_abi/agents/EntitytoSPARQLAgent.py +952 -0
- naas_abi/agents/EntitytoSPARQLAgent_test.py +66 -0
- naas_abi/agents/KnowledgeGraphBuilderAgent.py +321 -0
- naas_abi/agents/KnowledgeGraphBuilderAgent_test.py +86 -0
- naas_abi/agents/OntologyEngineerAgent.py +115 -0
- naas_abi/agents/OntologyEngineerAgent_test.py +42 -0
- naas_abi/apps/oxigraph_admin/main.py +392 -0
- naas_abi/apps/oxigraph_admin/terminal_style.py +151 -0
- naas_abi/apps/sparql_terminal/main.py +68 -0
- naas_abi/apps/sparql_terminal/terminal_style.py +236 -0
- naas_abi/apps/terminal_agent/main.py +553 -0
- naas_abi/apps/terminal_agent/terminal_style.py +175 -0
- naas_abi/cli.py +714 -0
- naas_abi/mappings.py +83 -0
- naas_abi/models/airgap_gemma.py +220 -0
- naas_abi/models/airgap_qwen.py +24 -0
- naas_abi/models/default.py +23 -0
- naas_abi/models/gpt_4_1.py +25 -0
- naas_abi/pipelines/AIAgentOntologyGenerationPipeline.py +635 -0
- naas_abi/pipelines/AIAgentOntologyGenerationPipeline_test.py +133 -0
- naas_abi/pipelines/AddIndividualPipeline.py +215 -0
- naas_abi/pipelines/AddIndividualPipeline_test.py +66 -0
- naas_abi/pipelines/InsertDataSPARQLPipeline.py +197 -0
- naas_abi/pipelines/InsertDataSPARQLPipeline_test.py +96 -0
- naas_abi/pipelines/MergeIndividualsPipeline.py +245 -0
- naas_abi/pipelines/MergeIndividualsPipeline_test.py +98 -0
- naas_abi/pipelines/RemoveIndividualPipeline.py +166 -0
- naas_abi/pipelines/RemoveIndividualPipeline_test.py +58 -0
- naas_abi/pipelines/UpdateCommercialOrganizationPipeline.py +198 -0
- naas_abi/pipelines/UpdateDataPropertyPipeline.py +175 -0
- naas_abi/pipelines/UpdateLegalNamePipeline.py +107 -0
- naas_abi/pipelines/UpdateLinkedInPagePipeline.py +179 -0
- naas_abi/pipelines/UpdatePersonPipeline.py +184 -0
- naas_abi/pipelines/UpdateSkillPipeline.py +118 -0
- naas_abi/pipelines/UpdateTickerPipeline.py +104 -0
- naas_abi/pipelines/UpdateWebsitePipeline.py +106 -0
- naas_abi/triggers.py +131 -0
- naas_abi/workflows/AgentRecommendationWorkflow.py +321 -0
- naas_abi/workflows/AgentRecommendationWorkflow_test.py +160 -0
- naas_abi/workflows/ArtificialAnalysisWorkflow.py +337 -0
- naas_abi/workflows/ArtificialAnalysisWorkflow_test.py +57 -0
- naas_abi/workflows/ConvertOntologyGraphToYamlWorkflow.py +210 -0
- naas_abi/workflows/ConvertOntologyGraphToYamlWorkflow_test.py +78 -0
- naas_abi/workflows/CreateClassOntologyYamlWorkflow.py +208 -0
- naas_abi/workflows/CreateClassOntologyYamlWorkflow_test.py +65 -0
- naas_abi/workflows/CreateIndividualOntologyYamlWorkflow.py +183 -0
- naas_abi/workflows/CreateIndividualOntologyYamlWorkflow_test.py +86 -0
- naas_abi/workflows/ExportGraphInstancesToExcelWorkflow.py +450 -0
- naas_abi/workflows/ExportGraphInstancesToExcelWorkflow_test.py +33 -0
- naas_abi/workflows/GetObjectPropertiesFromClassWorkflow.py +385 -0
- naas_abi/workflows/GetObjectPropertiesFromClassWorkflow_test.py +57 -0
- naas_abi/workflows/GetSubjectGraphWorkflow.py +84 -0
- naas_abi/workflows/GetSubjectGraphWorkflow_test.py +71 -0
- naas_abi/workflows/SearchIndividualWorkflow.py +190 -0
- naas_abi/workflows/SearchIndividualWorkflow_test.py +98 -0
- naas_abi-1.0.0.dist-info/METADATA +9 -0
- naas_abi-1.0.0.dist-info/RECORD +62 -0
- naas_abi-1.0.0.dist-info/WHEEL +5 -0
- naas_abi-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Oxigraph Administrative Interface
|
|
3
|
+
Provides terminal-based management and monitoring for Oxigraph triple store
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import subprocess
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
from naas_abi import services
|
|
10
|
+
from naas_abi.apps.oxigraph_admin.terminal_style import (
|
|
11
|
+
clear_screen,
|
|
12
|
+
get_user_input,
|
|
13
|
+
print_divider,
|
|
14
|
+
print_error_message,
|
|
15
|
+
print_menu_options,
|
|
16
|
+
print_status_info,
|
|
17
|
+
print_success_message,
|
|
18
|
+
print_welcome_message,
|
|
19
|
+
)
|
|
20
|
+
from rich.console import Console
|
|
21
|
+
from rich.table import Table
|
|
22
|
+
|
|
23
|
+
console = Console()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class OxigraphAdmin:
|
|
27
|
+
def __init__(self):
|
|
28
|
+
self.oxigraph_url = "http://localhost:7878"
|
|
29
|
+
self.triple_store_service = services.triple_store_service
|
|
30
|
+
self.query_templates = self._init_query_templates()
|
|
31
|
+
|
|
32
|
+
def _init_query_templates(self):
|
|
33
|
+
"""Initialize SPARQL query templates for common exploration tasks"""
|
|
34
|
+
return {
|
|
35
|
+
"1": {
|
|
36
|
+
"name": "š Data Overview - Entity Types & Counts",
|
|
37
|
+
"description": "Show all entity types and their counts",
|
|
38
|
+
"query": """SELECT DISTINCT ?type (COUNT(*) AS ?count) WHERE {
|
|
39
|
+
?s a ?type
|
|
40
|
+
} GROUP BY ?type ORDER BY DESC(?count)""",
|
|
41
|
+
},
|
|
42
|
+
"2": {
|
|
43
|
+
"name": "šļø Class Hierarchy - Top Level Classes",
|
|
44
|
+
"description": "Show top-level OWL classes in the ontology",
|
|
45
|
+
"query": """PREFIX owl: <http://www.w3.org/2002/07/owl#>
|
|
46
|
+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
47
|
+
|
|
48
|
+
SELECT ?class ?label WHERE {
|
|
49
|
+
?class a owl:Class .
|
|
50
|
+
OPTIONAL { ?class rdfs:label ?label }
|
|
51
|
+
FILTER NOT EXISTS { ?class rdfs:subClassOf ?parent }
|
|
52
|
+
} ORDER BY ?label LIMIT 20""",
|
|
53
|
+
},
|
|
54
|
+
"3": {
|
|
55
|
+
"name": "š Property Relations - Object Properties",
|
|
56
|
+
"description": "Explore object properties and their domains/ranges",
|
|
57
|
+
"query": """PREFIX owl: <http://www.w3.org/2002/07/owl#>
|
|
58
|
+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
59
|
+
|
|
60
|
+
SELECT ?property ?label ?domain ?range WHERE {
|
|
61
|
+
?property a owl:ObjectProperty .
|
|
62
|
+
OPTIONAL { ?property rdfs:label ?label }
|
|
63
|
+
OPTIONAL { ?property rdfs:domain ?domain }
|
|
64
|
+
OPTIONAL { ?property rdfs:range ?range }
|
|
65
|
+
} ORDER BY ?label LIMIT 20""",
|
|
66
|
+
},
|
|
67
|
+
"4": {
|
|
68
|
+
"name": "š„ ABI Entities - System Components",
|
|
69
|
+
"description": "Show ABI-specific entities and components",
|
|
70
|
+
"query": """PREFIX abi: <http://ontology.naas.ai/abi#>
|
|
71
|
+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
72
|
+
|
|
73
|
+
SELECT ?entity ?type ?label WHERE {
|
|
74
|
+
?entity ?p ?o .
|
|
75
|
+
FILTER(STRSTARTS(STR(?entity), "http://ontology.naas.ai/abi"))
|
|
76
|
+
OPTIONAL { ?entity a ?type }
|
|
77
|
+
OPTIONAL { ?entity rdfs:label ?label }
|
|
78
|
+
} LIMIT 30""",
|
|
79
|
+
},
|
|
80
|
+
"5": {
|
|
81
|
+
"name": "š§ Intent Mapping - System Components",
|
|
82
|
+
"description": "Explore intent mapping system components",
|
|
83
|
+
"query": """PREFIX intent: <http://ontology.naas.ai/intentMapping#>
|
|
84
|
+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
85
|
+
|
|
86
|
+
SELECT ?intent ?label ?description WHERE {
|
|
87
|
+
?intent ?p ?o .
|
|
88
|
+
FILTER(STRSTARTS(STR(?intent), "http://ontology.naas.ai/intentMapping"))
|
|
89
|
+
OPTIONAL { ?intent rdfs:label ?label }
|
|
90
|
+
OPTIONAL { ?intent rdfs:comment ?description }
|
|
91
|
+
} LIMIT 20""",
|
|
92
|
+
},
|
|
93
|
+
"6": {
|
|
94
|
+
"name": "š Namespace Analysis - Data Distribution",
|
|
95
|
+
"description": "Analyze data distribution across namespaces",
|
|
96
|
+
"query": """SELECT ?namespace (COUNT(*) AS ?triples) WHERE {
|
|
97
|
+
?s ?p ?o .
|
|
98
|
+
BIND(REPLACE(STR(?s), "(#|/)[^#/]*$", "$1") AS ?namespace)
|
|
99
|
+
} GROUP BY ?namespace ORDER BY DESC(?triples) LIMIT 15""",
|
|
100
|
+
},
|
|
101
|
+
"7": {
|
|
102
|
+
"name": "š Custom Search - Find by Term",
|
|
103
|
+
"description": "Search for entities containing a specific term",
|
|
104
|
+
"query": """PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
105
|
+
|
|
106
|
+
SELECT ?entity ?label ?type WHERE {
|
|
107
|
+
?entity ?p ?o .
|
|
108
|
+
?entity rdfs:label ?label .
|
|
109
|
+
OPTIONAL { ?entity a ?type }
|
|
110
|
+
FILTER(CONTAINS(LCASE(STR(?label)), "SEARCH_TERM"))
|
|
111
|
+
} LIMIT 20""",
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
def check_oxigraph_health(self):
|
|
116
|
+
"""Check if Oxigraph is running and accessible"""
|
|
117
|
+
try:
|
|
118
|
+
response = requests.get(
|
|
119
|
+
f"{self.oxigraph_url}/query?query=SELECT%20%3Fs%20WHERE%20%7B%3Fs%20%3Fp%20%3Fo%7D%20LIMIT%201",
|
|
120
|
+
timeout=5,
|
|
121
|
+
)
|
|
122
|
+
return response.status_code == 200
|
|
123
|
+
except Exception:
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
def get_triple_count(self):
|
|
127
|
+
"""Get the total number of triples in the store"""
|
|
128
|
+
try:
|
|
129
|
+
results = self.triple_store_service.query(
|
|
130
|
+
"SELECT (COUNT(*) AS ?count) WHERE { ?s ?p ?o }"
|
|
131
|
+
)
|
|
132
|
+
result_list = list(results)
|
|
133
|
+
if result_list:
|
|
134
|
+
return int(result_list[0].count)
|
|
135
|
+
except Exception:
|
|
136
|
+
pass
|
|
137
|
+
return 0
|
|
138
|
+
|
|
139
|
+
def get_class_count(self):
|
|
140
|
+
"""Get the number of OWL classes"""
|
|
141
|
+
try:
|
|
142
|
+
results = self.triple_store_service.query("""
|
|
143
|
+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
|
|
144
|
+
SELECT (COUNT(DISTINCT ?class) AS ?count) WHERE {
|
|
145
|
+
?class a owl:Class
|
|
146
|
+
}
|
|
147
|
+
""")
|
|
148
|
+
result_list = list(results)
|
|
149
|
+
if result_list:
|
|
150
|
+
return int(result_list[0].count)
|
|
151
|
+
except Exception:
|
|
152
|
+
pass
|
|
153
|
+
return 0
|
|
154
|
+
|
|
155
|
+
def get_property_count(self):
|
|
156
|
+
"""Get the number of properties"""
|
|
157
|
+
try:
|
|
158
|
+
results = self.triple_store_service.query("""
|
|
159
|
+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
|
|
160
|
+
SELECT (COUNT(DISTINCT ?prop) AS ?count) WHERE {
|
|
161
|
+
{ ?prop a owl:ObjectProperty } UNION
|
|
162
|
+
{ ?prop a owl:DatatypeProperty }
|
|
163
|
+
}
|
|
164
|
+
""")
|
|
165
|
+
result_list = list(results)
|
|
166
|
+
if result_list:
|
|
167
|
+
return int(result_list[0].count)
|
|
168
|
+
except Exception:
|
|
169
|
+
pass
|
|
170
|
+
return 0
|
|
171
|
+
|
|
172
|
+
def display_dashboard(self):
|
|
173
|
+
"""Display the main dashboard"""
|
|
174
|
+
clear_screen()
|
|
175
|
+
print_welcome_message("š§ Oxigraph Administrative Interface")
|
|
176
|
+
|
|
177
|
+
# Health check
|
|
178
|
+
is_healthy = self.check_oxigraph_health()
|
|
179
|
+
if is_healthy:
|
|
180
|
+
print_status_info("ā
Oxigraph is running and accessible")
|
|
181
|
+
else:
|
|
182
|
+
print_error_message("ā Oxigraph appears to be down or unreachable")
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
print_divider()
|
|
186
|
+
|
|
187
|
+
# Statistics
|
|
188
|
+
console.print(
|
|
189
|
+
"š [bold bright_cyan]Knowledge Graph Statistics[/bold bright_cyan]"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
stats_table = Table(show_header=True, header_style="bold magenta")
|
|
193
|
+
stats_table.add_column("Metric", style="cyan", no_wrap=True)
|
|
194
|
+
stats_table.add_column("Count", style="green", justify="right")
|
|
195
|
+
|
|
196
|
+
triple_count = self.get_triple_count()
|
|
197
|
+
class_count = self.get_class_count()
|
|
198
|
+
property_count = self.get_property_count()
|
|
199
|
+
|
|
200
|
+
stats_table.add_row("Total Triples", f"{triple_count:,}")
|
|
201
|
+
stats_table.add_row("OWL Classes", f"{class_count:,}")
|
|
202
|
+
stats_table.add_row("Properties", f"{property_count:,}")
|
|
203
|
+
stats_table.add_row("Endpoint", self.oxigraph_url)
|
|
204
|
+
|
|
205
|
+
console.print(stats_table)
|
|
206
|
+
print_divider()
|
|
207
|
+
|
|
208
|
+
def query_templates_menu(self):
|
|
209
|
+
"""Query template exploration menu"""
|
|
210
|
+
clear_screen()
|
|
211
|
+
console.print(
|
|
212
|
+
"šļø Query Templates - Explore Your Knowledge Graph",
|
|
213
|
+
style="bold bright_cyan",
|
|
214
|
+
)
|
|
215
|
+
print_divider()
|
|
216
|
+
|
|
217
|
+
# Show available templates
|
|
218
|
+
console.print("Available Templates:", style="bold bright_white")
|
|
219
|
+
for key, template in self.query_templates.items():
|
|
220
|
+
console.print(f" {key}. {template['name']}", style="white")
|
|
221
|
+
console.print(f" {template['description']}", style="dim")
|
|
222
|
+
|
|
223
|
+
print_divider()
|
|
224
|
+
|
|
225
|
+
choice = get_user_input("Select template (1-7) or 'back'")
|
|
226
|
+
|
|
227
|
+
if choice.lower() == "back":
|
|
228
|
+
return
|
|
229
|
+
elif choice in self.query_templates:
|
|
230
|
+
template = self.query_templates[choice]
|
|
231
|
+
console.print(f"\n[bold cyan]{template['name']}[/bold cyan]")
|
|
232
|
+
console.print(f"[bright_black]{template['query']}[/bright_black]")
|
|
233
|
+
|
|
234
|
+
# Ask if user wants to execute the query
|
|
235
|
+
execute = get_user_input("Execute this query? (y/n)")
|
|
236
|
+
if execute.lower() == "y":
|
|
237
|
+
try:
|
|
238
|
+
print_status_info("Executing query...")
|
|
239
|
+
query = template["query"]
|
|
240
|
+
|
|
241
|
+
# Handle custom search template
|
|
242
|
+
if "SEARCH_TERM" in query:
|
|
243
|
+
search_term = get_user_input("Enter search term")
|
|
244
|
+
query = query.replace("SEARCH_TERM", search_term.lower())
|
|
245
|
+
|
|
246
|
+
results = self.triple_store_service.query(query)
|
|
247
|
+
console.print("[green]Query executed successfully![/green]")
|
|
248
|
+
console.print(f"[bright_black]{results}[/bright_black]")
|
|
249
|
+
|
|
250
|
+
except Exception as e:
|
|
251
|
+
print_error_message(f"Query execution failed: {e}")
|
|
252
|
+
|
|
253
|
+
# Wait for user to review results
|
|
254
|
+
get_user_input("Press Enter to continue...")
|
|
255
|
+
else:
|
|
256
|
+
print_error_message("Invalid template selection")
|
|
257
|
+
get_user_input("Press Enter to continue...")
|
|
258
|
+
|
|
259
|
+
def service_control_menu(self):
|
|
260
|
+
"""Service control and monitoring"""
|
|
261
|
+
clear_screen()
|
|
262
|
+
console.print("āļø Service Control", style="bold bright_cyan")
|
|
263
|
+
print_divider()
|
|
264
|
+
|
|
265
|
+
print_menu_options(
|
|
266
|
+
[
|
|
267
|
+
"1. Restart Oxigraph",
|
|
268
|
+
"2. Check Docker status",
|
|
269
|
+
"3. View Oxigraph logs",
|
|
270
|
+
"4. Back to main menu",
|
|
271
|
+
]
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
choice = get_user_input("Select option")
|
|
275
|
+
|
|
276
|
+
if choice == "1":
|
|
277
|
+
print_status_info("Restarting Oxigraph...")
|
|
278
|
+
try:
|
|
279
|
+
subprocess.run(
|
|
280
|
+
["docker", "compose", "--profile", "dev", "restart", "oxigraph"],
|
|
281
|
+
check=True,
|
|
282
|
+
)
|
|
283
|
+
print_success_message("Oxigraph restarted successfully")
|
|
284
|
+
except subprocess.CalledProcessError:
|
|
285
|
+
print_error_message("Failed to restart Oxigraph")
|
|
286
|
+
|
|
287
|
+
elif choice == "2":
|
|
288
|
+
print_status_info("Checking Docker services...")
|
|
289
|
+
try:
|
|
290
|
+
result = subprocess.run(
|
|
291
|
+
["docker", "compose", "ps"], capture_output=True, text=True
|
|
292
|
+
)
|
|
293
|
+
console.print(result.stdout)
|
|
294
|
+
except subprocess.CalledProcessError:
|
|
295
|
+
print_error_message("Failed to check Docker status")
|
|
296
|
+
|
|
297
|
+
elif choice == "3":
|
|
298
|
+
print_status_info("Showing Oxigraph logs...")
|
|
299
|
+
try:
|
|
300
|
+
subprocess.run(["docker", "compose", "logs", "--tail=50", "oxigraph"])
|
|
301
|
+
except subprocess.CalledProcessError:
|
|
302
|
+
print_error_message("Failed to get logs")
|
|
303
|
+
|
|
304
|
+
if choice in ["1", "2", "3"]:
|
|
305
|
+
get_user_input("Press Enter to continue...")
|
|
306
|
+
|
|
307
|
+
def data_management_menu(self):
|
|
308
|
+
"""Data import/export and management"""
|
|
309
|
+
clear_screen()
|
|
310
|
+
console.print("š Data Management", style="bold bright_cyan")
|
|
311
|
+
print_divider()
|
|
312
|
+
|
|
313
|
+
print_menu_options(
|
|
314
|
+
[
|
|
315
|
+
"1. Show recent changes",
|
|
316
|
+
"2. Export data",
|
|
317
|
+
"3. Data validation",
|
|
318
|
+
"4. Back to main menu",
|
|
319
|
+
]
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
choice = get_user_input("Select option")
|
|
323
|
+
|
|
324
|
+
if choice == "1":
|
|
325
|
+
# This would show recent triples or changes
|
|
326
|
+
print_status_info("Feature coming soon...")
|
|
327
|
+
get_user_input("Press Enter to continue...")
|
|
328
|
+
|
|
329
|
+
def run(self):
|
|
330
|
+
"""Run the Oxigraph admin interface"""
|
|
331
|
+
while True:
|
|
332
|
+
self.display_dashboard()
|
|
333
|
+
|
|
334
|
+
print_menu_options(
|
|
335
|
+
[
|
|
336
|
+
"1. Refresh dashboard",
|
|
337
|
+
"2. Query templates & examples",
|
|
338
|
+
"3. Service control",
|
|
339
|
+
"4. Data management",
|
|
340
|
+
"5. Open SPARQL terminal",
|
|
341
|
+
"6. Open YasGUI web interface",
|
|
342
|
+
"7. Open unified Knowledge Graph Explorer",
|
|
343
|
+
"8. Exit",
|
|
344
|
+
]
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
choice = get_user_input("Select option")
|
|
348
|
+
|
|
349
|
+
if choice == "1":
|
|
350
|
+
continue
|
|
351
|
+
elif choice == "2":
|
|
352
|
+
self.query_templates_menu()
|
|
353
|
+
elif choice == "3":
|
|
354
|
+
self.service_control_menu()
|
|
355
|
+
elif choice == "4":
|
|
356
|
+
self.data_management_menu()
|
|
357
|
+
elif choice == "5":
|
|
358
|
+
console.print("Opening SPARQL terminal...")
|
|
359
|
+
subprocess.run(
|
|
360
|
+
[
|
|
361
|
+
"uv",
|
|
362
|
+
"run",
|
|
363
|
+
"python",
|
|
364
|
+
"-m",
|
|
365
|
+
"src.core.abi.apps.sparql_terminal.main",
|
|
366
|
+
]
|
|
367
|
+
)
|
|
368
|
+
break
|
|
369
|
+
elif choice == "6":
|
|
370
|
+
console.print("Opening YasGUI web interface...")
|
|
371
|
+
console.print("Visit: http://localhost:3000")
|
|
372
|
+
break
|
|
373
|
+
|
|
374
|
+
elif choice == "7":
|
|
375
|
+
console.print("Opening unified Knowledge Graph Explorer...")
|
|
376
|
+
console.print("Visit: http://localhost:7878/explorer/")
|
|
377
|
+
console.print(
|
|
378
|
+
"Features: Dashboard + iframe YasGUI + Templates + ABI Brain"
|
|
379
|
+
)
|
|
380
|
+
break
|
|
381
|
+
elif choice == "8":
|
|
382
|
+
print_success_message("Goodbye!")
|
|
383
|
+
break
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def main():
|
|
387
|
+
admin = OxigraphAdmin()
|
|
388
|
+
admin.run()
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
if __name__ == "__main__":
|
|
392
|
+
main()
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from rich.console import Console
|
|
2
|
+
from rich.panel import Panel
|
|
3
|
+
from rich.text import Text
|
|
4
|
+
from rich.align import Align
|
|
5
|
+
from rich.prompt import Prompt
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
console = Console()
|
|
9
|
+
|
|
10
|
+
def set_terminal_title():
|
|
11
|
+
"""Set the terminal window title"""
|
|
12
|
+
try:
|
|
13
|
+
print("\33]0;Oxigraph Admin\a", end="", flush=True)
|
|
14
|
+
except Exception:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
def print_welcome_message():
|
|
18
|
+
"""Print the welcome message for Oxigraph Admin"""
|
|
19
|
+
set_terminal_title()
|
|
20
|
+
|
|
21
|
+
title = Text("š§ OXIGRAPH ADMIN", style="bold bright_cyan")
|
|
22
|
+
subtitle = Text("Triple Store Administration & Monitoring", style="dim")
|
|
23
|
+
|
|
24
|
+
content = Align.center(f"{title}\n{subtitle}")
|
|
25
|
+
|
|
26
|
+
panel = Panel(
|
|
27
|
+
content,
|
|
28
|
+
border_style="bright_cyan",
|
|
29
|
+
padding=(1, 2),
|
|
30
|
+
title="[bold]ABI Infrastructure[/bold]",
|
|
31
|
+
title_align="center"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
console.print()
|
|
35
|
+
console.print(panel)
|
|
36
|
+
console.print()
|
|
37
|
+
|
|
38
|
+
def print_divider():
|
|
39
|
+
"""Print a visual divider"""
|
|
40
|
+
console.print("ā" * console.width, style="dim")
|
|
41
|
+
console.print()
|
|
42
|
+
|
|
43
|
+
def print_status_info(text):
|
|
44
|
+
"""Print status information"""
|
|
45
|
+
console.print(f"ā¹ļø {text}", style="bright_blue")
|
|
46
|
+
|
|
47
|
+
def print_success_message(text):
|
|
48
|
+
"""Print success message"""
|
|
49
|
+
console.print(f"ā
{text}", style="bright_green")
|
|
50
|
+
|
|
51
|
+
def print_error_message(text):
|
|
52
|
+
"""Print error message"""
|
|
53
|
+
console.print(f"ā {text}", style="bright_red")
|
|
54
|
+
|
|
55
|
+
def print_warning_message(text):
|
|
56
|
+
"""Print warning message"""
|
|
57
|
+
console.print(f"ā ļø {text}", style="bright_yellow")
|
|
58
|
+
|
|
59
|
+
def print_menu_options(options):
|
|
60
|
+
"""Print menu options"""
|
|
61
|
+
console.print("\nš Available Operations:", style="bold bright_white")
|
|
62
|
+
for option in options:
|
|
63
|
+
console.print(f" {option}", style="white")
|
|
64
|
+
console.print()
|
|
65
|
+
|
|
66
|
+
def get_user_input(prompt_text="Enter your choice"):
|
|
67
|
+
"""Get user input with styled prompt"""
|
|
68
|
+
try:
|
|
69
|
+
return Prompt.ask(f"[bold bright_cyan]>[/bold bright_cyan] {prompt_text}")
|
|
70
|
+
except KeyboardInterrupt:
|
|
71
|
+
console.print("\n\nš Ctrl+C pressed. Exiting Oxigraph Admin...", style="bright_red")
|
|
72
|
+
return "exit"
|
|
73
|
+
except EOFError:
|
|
74
|
+
return "exit"
|
|
75
|
+
|
|
76
|
+
def clear_screen():
|
|
77
|
+
"""Clear the terminal screen"""
|
|
78
|
+
console.clear()
|
|
79
|
+
|
|
80
|
+
def print_health_status(healthy: bool, message: str):
|
|
81
|
+
"""Print health status with appropriate styling"""
|
|
82
|
+
if healthy:
|
|
83
|
+
console.print(f"š¢ Status: [bright_green]{message}[/bright_green]")
|
|
84
|
+
else:
|
|
85
|
+
console.print(f"š“ Status: [bright_red]{message}[/bright_red]")
|
|
86
|
+
|
|
87
|
+
def print_container_info(container_data):
|
|
88
|
+
"""Print container information in a formatted way"""
|
|
89
|
+
from rich.table import Table
|
|
90
|
+
|
|
91
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
92
|
+
table.add_column("Container", style="cyan", no_wrap=True)
|
|
93
|
+
table.add_column("Status", style="green")
|
|
94
|
+
table.add_column("Ports", style="yellow")
|
|
95
|
+
table.add_column("Created", style="blue")
|
|
96
|
+
|
|
97
|
+
for container in container_data:
|
|
98
|
+
name = container.get('Name', 'Unknown')
|
|
99
|
+
status = container.get('State', 'Unknown')
|
|
100
|
+
ports = container.get('Ports', 'None')
|
|
101
|
+
created = container.get('Created', 'Unknown')
|
|
102
|
+
|
|
103
|
+
# Add status emoji
|
|
104
|
+
if status.lower() == 'running':
|
|
105
|
+
status = f"š¢ {status}"
|
|
106
|
+
elif status.lower() == 'exited':
|
|
107
|
+
status = f"š“ {status}"
|
|
108
|
+
else:
|
|
109
|
+
status = f"š” {status}"
|
|
110
|
+
|
|
111
|
+
table.add_row(name, status, ports, created)
|
|
112
|
+
|
|
113
|
+
console.print(table)
|
|
114
|
+
|
|
115
|
+
def print_performance_metrics(metrics):
|
|
116
|
+
"""Print performance metrics"""
|
|
117
|
+
console.print("\nš Performance Metrics:", style="bold bright_white")
|
|
118
|
+
|
|
119
|
+
for key, value in metrics.items():
|
|
120
|
+
# Format different types of metrics
|
|
121
|
+
if 'time' in key.lower() or 'latency' in key.lower():
|
|
122
|
+
console.print(f" ā±ļø {key}: [yellow]{value}ms[/yellow]")
|
|
123
|
+
elif 'count' in key.lower() or 'total' in key.lower():
|
|
124
|
+
console.print(f" š {key}: [green]{value:,}[/green]")
|
|
125
|
+
elif 'memory' in key.lower() or 'size' in key.lower():
|
|
126
|
+
console.print(f" š¾ {key}: [blue]{value}[/blue]")
|
|
127
|
+
else:
|
|
128
|
+
console.print(f" š {key}: [white]{value}[/white]")
|
|
129
|
+
|
|
130
|
+
def print_data_stats(stats):
|
|
131
|
+
"""Print data statistics"""
|
|
132
|
+
console.print("\nš Data Statistics:", style="bold bright_white")
|
|
133
|
+
|
|
134
|
+
for key, value in stats.items():
|
|
135
|
+
if isinstance(value, int):
|
|
136
|
+
console.print(f" ⢠{key}: [cyan]{value:,}[/cyan]")
|
|
137
|
+
else:
|
|
138
|
+
console.print(f" ⢠{key}: [white]{value}[/white]")
|
|
139
|
+
|
|
140
|
+
def confirmation_prompt(message: str, danger: bool = False) -> bool:
|
|
141
|
+
"""Get confirmation from user for potentially dangerous operations"""
|
|
142
|
+
style = "bright_red" if danger else "bright_yellow"
|
|
143
|
+
|
|
144
|
+
if danger:
|
|
145
|
+
console.print(f"ā ļø [bold {style}]DANGER: {message}[/bold {style}]")
|
|
146
|
+
response = Prompt.ask("Type 'YES' to confirm", default="NO")
|
|
147
|
+
return response.upper() == "YES"
|
|
148
|
+
else:
|
|
149
|
+
console.print(f"ā {message}")
|
|
150
|
+
response = Prompt.ask("Continue?", choices=["y", "n"], default="n")
|
|
151
|
+
return response.lower() == "y"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from naas_abi import services
|
|
2
|
+
from naas_abi.apps.sparql_terminal.terminal_style import (
|
|
3
|
+
clear_screen,
|
|
4
|
+
get_user_input,
|
|
5
|
+
print_divider,
|
|
6
|
+
print_query,
|
|
7
|
+
print_query_error,
|
|
8
|
+
print_query_result,
|
|
9
|
+
print_system_message,
|
|
10
|
+
print_welcome_message,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SPARQLTerminal:
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.triple_store_service = services.triple_store_service
|
|
17
|
+
|
|
18
|
+
def execute_query(self, query):
|
|
19
|
+
"""Execute a SPARQL query and return the results"""
|
|
20
|
+
try:
|
|
21
|
+
results = self.triple_store_service.query(query)
|
|
22
|
+
return results
|
|
23
|
+
except Exception as e:
|
|
24
|
+
raise Exception(f"Error executing query: {str(e)}")
|
|
25
|
+
|
|
26
|
+
def run(self):
|
|
27
|
+
"""Run the SPARQL terminal interface"""
|
|
28
|
+
clear_screen()
|
|
29
|
+
print_welcome_message()
|
|
30
|
+
print_divider()
|
|
31
|
+
|
|
32
|
+
while True:
|
|
33
|
+
user_input = get_user_input()
|
|
34
|
+
|
|
35
|
+
if user_input.lower() == "exit":
|
|
36
|
+
print_system_message("Goodbye!")
|
|
37
|
+
return
|
|
38
|
+
elif user_input.lower() == "help":
|
|
39
|
+
print_welcome_message()
|
|
40
|
+
continue
|
|
41
|
+
elif user_input.lower() == "clear":
|
|
42
|
+
clear_screen()
|
|
43
|
+
continue
|
|
44
|
+
elif not user_input.strip():
|
|
45
|
+
continue
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
# Print the query being executed
|
|
49
|
+
print_query(user_input)
|
|
50
|
+
print_divider()
|
|
51
|
+
|
|
52
|
+
# Execute the query and print results
|
|
53
|
+
results = self.execute_query(user_input)
|
|
54
|
+
print_query_result(results)
|
|
55
|
+
print_divider()
|
|
56
|
+
|
|
57
|
+
except Exception as e:
|
|
58
|
+
print_query_error(str(e))
|
|
59
|
+
print_divider()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def main():
|
|
63
|
+
terminal = SPARQLTerminal()
|
|
64
|
+
terminal.run()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
main()
|