@@ -2,7 +2,7 @@ use ::ontoenv::api::{OntoEnv as OntoEnvRs, ResolveTarget};
22use :: ontoenv:: config;
33use :: ontoenv:: consts:: { IMPORTS , ONTOLOGY , TYPE } ;
44use :: ontoenv:: ToUriString ;
5- use :: ontoenv:: ontology:: { OntologyLocation , GraphIdentifier } ;
5+ use :: ontoenv:: ontology:: { Ontology as OntologyRs , OntologyLocation , GraphIdentifier } ;
66use :: ontoenv:: transform;
77use anyhow:: Error ;
88use oxigraph:: model:: { BlankNode , Literal , NamedNode , SubjectRef , Term } ;
@@ -11,7 +11,7 @@ use pyo3::{
1111 types:: { IntoPyDict , PyString , PyTuple } ,
1212} ;
1313use std:: borrow:: Borrow ;
14- use std:: collections:: HashSet ;
14+ use std:: collections:: { HashMap , HashSet } ;
1515use std:: path:: PathBuf ;
1616use std:: sync:: { Arc , Mutex , Once } ;
1717
@@ -171,6 +171,67 @@ impl Config {
171171}
172172
173173
174+ #[ pyclass( name = "Ontology" ) ]
175+ #[ derive( Clone ) ]
176+ struct PyOntology {
177+ inner : OntologyRs ,
178+ }
179+
180+ #[ pymethods]
181+ impl PyOntology {
182+ #[ getter]
183+ fn id ( & self ) -> PyResult < String > {
184+ Ok ( self . inner . id ( ) . to_uri_string ( ) )
185+ }
186+
187+ #[ getter]
188+ fn name ( & self ) -> PyResult < String > {
189+ Ok ( self . inner . name ( ) . to_uri_string ( ) )
190+ }
191+
192+ #[ getter]
193+ fn imports ( & self ) -> PyResult < Vec < String > > {
194+ Ok ( self
195+ . inner
196+ . imports
197+ . iter ( )
198+ . map ( |i| i. to_uri_string ( ) )
199+ . collect ( ) )
200+ }
201+
202+ #[ getter]
203+ fn location ( & self ) -> PyResult < Option < String > > {
204+ Ok ( self . inner . location ( ) . map ( |l| l. to_string ( ) ) )
205+ }
206+
207+ #[ getter]
208+ fn last_updated ( & self ) -> PyResult < Option < String > > {
209+ Ok ( self . inner . last_updated . map ( |dt| dt. to_rfc3339 ( ) ) )
210+ }
211+
212+ #[ getter]
213+ fn version_properties ( & self ) -> PyResult < HashMap < String , String > > {
214+ Ok ( self
215+ . inner
216+ . version_properties ( )
217+ . iter ( )
218+ . map ( |( k, v) | ( k. to_uri_string ( ) , v. clone ( ) ) )
219+ . collect ( ) )
220+ }
221+
222+ #[ getter]
223+ fn namespace_map ( & self ) -> PyResult < HashMap < String , String > > {
224+ Ok ( self . inner . namespace_map ( ) . clone ( ) )
225+ }
226+
227+ fn __repr__ ( & self ) -> PyResult < String > {
228+ Ok ( format ! (
229+ "<Ontology: {}>" ,
230+ self . inner. name( ) . to_uri_string( )
231+ ) )
232+ }
233+ }
234+
174235#[ pyclass]
175236struct OntoEnv {
176237 inner : Arc < Mutex < Option < OntoEnvRs > > > ,
@@ -544,7 +605,8 @@ impl OntoEnv {
544605 }
545606
546607 /// Add a new ontology to the OntoEnv
547- fn add ( & self , location : & Bound < ' _ , PyAny > ) -> PyResult < String > {
608+ #[ pyo3( signature = ( location, overwrite = false ) ) ]
609+ fn add ( & self , location : & Bound < ' _ , PyAny > , overwrite : bool ) -> PyResult < String > {
548610 let inner = self . inner . clone ( ) ;
549611 let mut guard = inner. lock ( ) . unwrap ( ) ;
550612 let env = guard. as_mut ( ) . ok_or_else ( || {
@@ -570,29 +632,32 @@ impl OntoEnv {
570632 . map_err ( |e| PyErr :: new :: < pyo3:: exceptions:: PyValueError , _ > ( e. to_string ( ) ) ) ?;
571633
572634 let location_to_add = OntologyLocation :: File ( path. clone ( ) ) ;
573- let result = env. add ( location_to_add, true ) ;
635+ let result = env. add ( location_to_add, overwrite ) ;
574636 let _ = std:: fs:: remove_file ( & path) ;
575637 result
576638 . map ( |id| id. to_uri_string ( ) )
577639 . map_err ( anyhow_to_pyerr)
578640 } else {
579641 let location =
580642 OntologyLocation :: from_str ( & location. to_string ( ) ) . map_err ( anyhow_to_pyerr) ?;
581- let graph_id = env. add ( location, true ) . map_err ( anyhow_to_pyerr) ?;
643+ let graph_id = env. add ( location, overwrite ) . map_err ( anyhow_to_pyerr) ?;
582644 Ok ( graph_id. to_uri_string ( ) )
583645 }
584646 }
585647
586648 /// Add a new ontology to the OntoEnv without exploring owl:imports.
587- fn add_no_imports ( & self , location : & Bound < ' _ , PyAny > ) -> PyResult < String > {
649+ #[ pyo3( signature = ( location, overwrite = false ) ) ]
650+ fn add_no_imports ( & self , location : & Bound < ' _ , PyAny > , overwrite : bool ) -> PyResult < String > {
588651 let inner = self . inner . clone ( ) ;
589652 let mut guard = inner. lock ( ) . unwrap ( ) ;
590653 let env = guard. as_mut ( ) . ok_or_else ( || {
591654 PyErr :: new :: < pyo3:: exceptions:: PyValueError , _ > ( "OntoEnv is closed" )
592655 } ) ?;
593656 let location =
594657 OntologyLocation :: from_str ( & location. to_string ( ) ) . map_err ( anyhow_to_pyerr) ?;
595- let graph_id = env. add_no_imports ( location, true ) . map_err ( anyhow_to_pyerr) ?;
658+ let graph_id = env
659+ . add_no_imports ( location, overwrite)
660+ . map_err ( anyhow_to_pyerr) ?;
596661 Ok ( graph_id. to_uri_string ( ) )
597662 }
598663
@@ -611,6 +676,26 @@ impl OntoEnv {
611676 Ok ( names)
612677 }
613678
679+ /// Get the ontology metadata with the given URI
680+ fn get_ontology ( & self , uri : & str ) -> PyResult < PyOntology > {
681+ let iri = NamedNode :: new ( uri)
682+ . map_err ( |e| PyErr :: new :: < pyo3:: exceptions:: PyValueError , _ > ( e. to_string ( ) ) ) ?;
683+ let inner = self . inner . clone ( ) ;
684+ let guard = inner. lock ( ) . unwrap ( ) ;
685+ let env = guard. as_ref ( ) . ok_or_else ( || {
686+ PyErr :: new :: < pyo3:: exceptions:: PyValueError , _ > ( "OntoEnv is closed" )
687+ } ) ?;
688+ let graphid = env
689+ . resolve ( ResolveTarget :: Graph ( iri. clone ( ) ) )
690+ . ok_or_else ( || {
691+ PyErr :: new :: < pyo3:: exceptions:: PyValueError , _ > ( format ! (
692+ "Failed to resolve graph for URI: {uri}"
693+ ) )
694+ } ) ?;
695+ let ont = env. get_ontology ( & graphid) . map_err ( anyhow_to_pyerr) ?;
696+ Ok ( PyOntology { inner : ont } )
697+ }
698+
614699 /// Get the graph with the given URI as an rdflib.Graph
615700 fn get_graph ( & self , py : Python , uri : & Bound < ' _ , PyString > ) -> PyResult < Py < PyAny > > {
616701 let rdflib = py. import ( "rdflib" ) ?;
@@ -861,6 +946,7 @@ impl OntoEnv {
861946fn ontoenv ( _py : Python , m : & Bound < ' _ , PyModule > ) -> PyResult < ( ) > {
862947 m. add_class :: < Config > ( ) ?;
863948 m. add_class :: < OntoEnv > ( ) ?;
949+ m. add_class :: < PyOntology > ( ) ?;
864950 // add version attribute
865951 m. add ( "version" , env ! ( "CARGO_PKG_VERSION" ) ) ?;
866952 Ok ( ( ) )
0 commit comments