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.
Files changed (62) hide show
  1. naas_abi/__init__.py +35 -0
  2. naas_abi/agents/AbiAgent.py +442 -0
  3. naas_abi/agents/AbiAgent_test.py +157 -0
  4. naas_abi/agents/EntitytoSPARQLAgent.py +952 -0
  5. naas_abi/agents/EntitytoSPARQLAgent_test.py +66 -0
  6. naas_abi/agents/KnowledgeGraphBuilderAgent.py +321 -0
  7. naas_abi/agents/KnowledgeGraphBuilderAgent_test.py +86 -0
  8. naas_abi/agents/OntologyEngineerAgent.py +115 -0
  9. naas_abi/agents/OntologyEngineerAgent_test.py +42 -0
  10. naas_abi/apps/oxigraph_admin/main.py +392 -0
  11. naas_abi/apps/oxigraph_admin/terminal_style.py +151 -0
  12. naas_abi/apps/sparql_terminal/main.py +68 -0
  13. naas_abi/apps/sparql_terminal/terminal_style.py +236 -0
  14. naas_abi/apps/terminal_agent/main.py +553 -0
  15. naas_abi/apps/terminal_agent/terminal_style.py +175 -0
  16. naas_abi/cli.py +714 -0
  17. naas_abi/mappings.py +83 -0
  18. naas_abi/models/airgap_gemma.py +220 -0
  19. naas_abi/models/airgap_qwen.py +24 -0
  20. naas_abi/models/default.py +23 -0
  21. naas_abi/models/gpt_4_1.py +25 -0
  22. naas_abi/pipelines/AIAgentOntologyGenerationPipeline.py +635 -0
  23. naas_abi/pipelines/AIAgentOntologyGenerationPipeline_test.py +133 -0
  24. naas_abi/pipelines/AddIndividualPipeline.py +215 -0
  25. naas_abi/pipelines/AddIndividualPipeline_test.py +66 -0
  26. naas_abi/pipelines/InsertDataSPARQLPipeline.py +197 -0
  27. naas_abi/pipelines/InsertDataSPARQLPipeline_test.py +96 -0
  28. naas_abi/pipelines/MergeIndividualsPipeline.py +245 -0
  29. naas_abi/pipelines/MergeIndividualsPipeline_test.py +98 -0
  30. naas_abi/pipelines/RemoveIndividualPipeline.py +166 -0
  31. naas_abi/pipelines/RemoveIndividualPipeline_test.py +58 -0
  32. naas_abi/pipelines/UpdateCommercialOrganizationPipeline.py +198 -0
  33. naas_abi/pipelines/UpdateDataPropertyPipeline.py +175 -0
  34. naas_abi/pipelines/UpdateLegalNamePipeline.py +107 -0
  35. naas_abi/pipelines/UpdateLinkedInPagePipeline.py +179 -0
  36. naas_abi/pipelines/UpdatePersonPipeline.py +184 -0
  37. naas_abi/pipelines/UpdateSkillPipeline.py +118 -0
  38. naas_abi/pipelines/UpdateTickerPipeline.py +104 -0
  39. naas_abi/pipelines/UpdateWebsitePipeline.py +106 -0
  40. naas_abi/triggers.py +131 -0
  41. naas_abi/workflows/AgentRecommendationWorkflow.py +321 -0
  42. naas_abi/workflows/AgentRecommendationWorkflow_test.py +160 -0
  43. naas_abi/workflows/ArtificialAnalysisWorkflow.py +337 -0
  44. naas_abi/workflows/ArtificialAnalysisWorkflow_test.py +57 -0
  45. naas_abi/workflows/ConvertOntologyGraphToYamlWorkflow.py +210 -0
  46. naas_abi/workflows/ConvertOntologyGraphToYamlWorkflow_test.py +78 -0
  47. naas_abi/workflows/CreateClassOntologyYamlWorkflow.py +208 -0
  48. naas_abi/workflows/CreateClassOntologyYamlWorkflow_test.py +65 -0
  49. naas_abi/workflows/CreateIndividualOntologyYamlWorkflow.py +183 -0
  50. naas_abi/workflows/CreateIndividualOntologyYamlWorkflow_test.py +86 -0
  51. naas_abi/workflows/ExportGraphInstancesToExcelWorkflow.py +450 -0
  52. naas_abi/workflows/ExportGraphInstancesToExcelWorkflow_test.py +33 -0
  53. naas_abi/workflows/GetObjectPropertiesFromClassWorkflow.py +385 -0
  54. naas_abi/workflows/GetObjectPropertiesFromClassWorkflow_test.py +57 -0
  55. naas_abi/workflows/GetSubjectGraphWorkflow.py +84 -0
  56. naas_abi/workflows/GetSubjectGraphWorkflow_test.py +71 -0
  57. naas_abi/workflows/SearchIndividualWorkflow.py +190 -0
  58. naas_abi/workflows/SearchIndividualWorkflow_test.py +98 -0
  59. naas_abi-1.0.0.dist-info/METADATA +9 -0
  60. naas_abi-1.0.0.dist-info/RECORD +62 -0
  61. naas_abi-1.0.0.dist-info/WHEEL +5 -0
  62. 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()