This commit is contained in:
2026-08-07 19:28:08 -04:00
parent 36a105edfc
commit 64a2c87346
4 changed files with 112 additions and 48 deletions
+1
View File
@@ -9,6 +9,7 @@ const GL_PREFIX: &str = "https://graphofliberty.org/";
const INFERENCE_GRAPH: GraphNameRef = GraphNameRef::NamedNode(NamedNodeRef::new_unchecked("https://graphofliberty.org/inference"));
const INPUT_GRAPH: GraphNameRef = GraphNameRef::NamedNode(NamedNodeRef::new_unchecked("https://graphofliberty.org/input"));
#[derive(Clone)]
pub struct InferenceEngine {
ontology: Store,
}
+100 -28
View File
@@ -3,7 +3,6 @@ use crate::rdf::ontology::{LabeledIri, Ontology};
use crate::rdf::term_helper::{TermHelper, TermHelperMut};
use crate::widget::iri_input::iri_input;
use crate::widget::navigation_area::navigation_area;
use gl_graph::vocab::rdac;
use gl_search::language;
use gl_search::{Schema, SearchDocument, SearchIndex, Value};
use http::StatusCode;
@@ -22,9 +21,11 @@ use ldp::reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use ldp::{RdfSource, RdfSourceUpdateResponse, ResourceRequestBuilder, SerializationOptions};
use oxigraph::io::RdfFormat;
use oxigraph::model::vocab::{rdf, rdfs};
use oxigraph::model::{BaseDirection, Dataset, NamedNode, NamedOrBlankNode, Quad, Term};
use oxigraph::model::{BaseDirection, Dataset, Graph, NamedNode, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, Term, TripleRef};
use oxigraph::store::Store;
use tracing::{debug_span, error, trace};
use gl_graph::{vocab, CurieHelper};
use gl_graph::CurieHelper;
use gl_graph::inference::InferenceEngine;
#[derive(Clone, Debug)]
pub(crate) enum Message {
@@ -38,7 +39,7 @@ pub(crate) enum Message {
ShowNewDocumentButtons,
HideNewDocumentButtons,
ShowError(String),
AddRow(Option<QuadKey>),
AddRow(Option<(QuadKey, RowState)>),
DeleteRow(QuadKey),
DeleteAllRows,
OpenQueryWindow(SearchResultClickAction),
@@ -65,6 +66,10 @@ pub(crate) enum Message {
NavigateForward,
ResetState,
SetReadOnly(bool),
SetInferredTypes(bool),
SetInferredProperties(bool),
RunInference,
SetInferredTriples(Dataset),
Event(Event),
}
@@ -93,13 +98,17 @@ pub(crate) struct Publisher {
http_client: ClientWithMiddleware,
curie_helper: CurieHelper,
ontology: Ontology,
inference_engine: InferenceEngine,
abbreviated_datatypes: Vec<String>,
window_id: window::Id,
url_input: String,
url_input_valid: bool,
navigator: Navigator<Url>,
document: RdfSource<KeyedDataset<RowState>>,
inferred_triples: Graph,
show_read_only: bool,
show_inferred_types: bool,
show_inferred_properties: bool,
hovered_row: Option<QuadKey>,
search_state: Option<SearchState>,
index: SearchIndex,
@@ -120,6 +129,8 @@ impl Publisher {
.expect("Failed to build ontology")
});
let inference_engine = InferenceEngine::new(ontology.store().clone());
let index = SearchIndex::builder()
.with_path("/home/alex/.local/share/org.graphofliberty.desktop/index")
.build()
@@ -152,13 +163,17 @@ impl Publisher {
http_client,
curie_helper,
ontology,
inference_engine,
abbreviated_datatypes,
window_id: id,
url_input: starting_url.to_string(),
url_input_valid: true,
navigator,
document,
inferred_triples: Graph::new(),
show_read_only: false,
show_inferred_types: false,
show_inferred_properties: false,
hovered_row: None,
search_state: None,
index,
@@ -234,9 +249,21 @@ impl Publisher {
let messages = document
.dataset()
.quads
.keys()
.map(|key| Message::AddRow(Some(key)));
task = Task::batch(messages.map(Task::done)).chain(Task::done(Message::ResetState));
.iter()
.map(|(key, quad)| {
let datatype_state = combo_box::State::new(
self.abbreviated_datatypes.clone(),
);
let state = RowState {
read_only: self.ontology.is_read_only(quad.as_ref()),
datatype_state,
};
Message::AddRow(Some((key, state)))
});
task = Task::batch(messages.map(Task::done))
.chain(Task::done(Message::RunInference))
.chain(Task::done(Message::ResetState));
self.document = document;
}
@@ -250,33 +277,21 @@ impl Publisher {
quad.object = Term::NamedNode(empty_node);
let key = self.document.dataset_mut().quads.insert(quad);
task = Task::done(Message::AddRow(Some(key)));
}
Message::AddRow(Some(key)) => {
let quad = self
.document
.dataset()
.quads
.get(key)
.expect("Failed to get quad from document");
let read_only = self.ontology.is_read_only(quad.as_ref());
let term = TermHelper::new(&quad.object);
let datatype = term
.datatype()
.map(|datatype| self.curie_helper.abbreviate(None, datatype.as_str())
.unwrap_or(datatype.as_str().to_string()));
let datatype_state = combo_box::State::new(
self.abbreviated_datatypes.clone(),
);
let state = RowState {
read_only,
read_only: false,
datatype_state,
};
task = Task::done(Message::AddRow(Some((key, state))));
}
Message::AddRow(Some((key, state))) => {
self.document
.dataset_mut()
.associated_data
.insert(key, state);
self.modified = true;
}
Message::DeleteRow(key) => {
@@ -472,7 +487,7 @@ impl Publisher {
let subject = NamedNode::new_unchecked(url.as_str());
self.document = RdfSource::new(url);
let template_quads = self
/*let template_quads = self
.ontology
.template_triples(class.as_ref(), subject.as_ref())
.map(|triples| {
@@ -490,7 +505,7 @@ impl Publisher {
let new_keys = self.document.dataset_mut().extend(template_quads.into_iter());
let messages = new_keys.map(|triple| Message::AddRow(Some(triple)));
task = task.chain(Task::batch(messages.map(Task::done)));
task = task.chain(Task::batch(messages.map(Task::done)));*/
}
Message::ResetState => {
self.modified = false;
@@ -526,6 +541,28 @@ impl Publisher {
Message::SetReadOnly(value) => {
self.show_read_only = value;
}
Message::SetInferredTypes(value) => {
self.show_inferred_types = value;
}
Message::SetInferredProperties(value) => {
self.show_inferred_properties = value;
}
Message::RunInference => {
let dataset = self.document
.dataset()
.quads
.iter()
.map(|(_, quad)| quad)
.collect();
let engine = self.inference_engine.clone();
task = Task::perform(tokio::task::spawn_blocking(move || {
engine.run(&dataset).unwrap()
}), |dataset| Message::SetInferredTriples(dataset.unwrap()));
}
Message::SetInferredTriples(dataset) => {
self.inferred_triples = Graph::from_iter(&dataset);
}
Message::Event(Event::KeyPressed {
key: keyboard::Key::Named(key::Named::Tab),
modifiers,
@@ -823,6 +860,29 @@ impl Publisher {
column(rows).into()
};
let inference_table = if self.show_inferred_properties {
let subject_column = table::column("Subject", |triple: TripleRef| {
let curie = if let NamedOrBlankNodeRef::NamedNode(subject) = triple.subject {
self.curie_helper.abbreviate(None, subject.as_str())
} else { None };
curie.map(text)
});
let predicate_column = table::column("Predicate", |triple: TripleRef| {
let predicate_info = self.ontology.info(&triple.predicate.into_owned(), &*language::ENGLISH_OR_UNTAGGED);
let label = if predicate_info.label.is_empty() {
self.curie_helper
.abbreviate(None, triple.predicate.as_str())
.unwrap_or(triple.predicate.as_str().to_string())
} else { predicate_info.label };
text(label)
});
let object_column = table::column("Object", |triple: TripleRef| text(triple.object.to_string()));
Some(table([subject_column, predicate_column, object_column], self.inferred_triples.iter()))
} else { None };
let save_button_base = button(text("\u{1f4be}"));
let save_button = if self.modified {
save_button_base.on_press(Message::SaveGraph(false))
@@ -830,10 +890,22 @@ impl Publisher {
save_button_base
};
let inferred_type_toggle = toggler(self.show_inferred_types)
.on_toggle(Message::SetInferredTypes);
let inferred_property_toggle = toggler(self.show_inferred_properties)
.on_toggle(Message::SetInferredProperties);
let read_only_toggle = toggler(self.show_read_only)
.on_toggle(Message::SetReadOnly);
let footer = row![
space::horizontal(),
text("Show Inferred Types"),
inferred_type_toggle,
space::horizontal(),
text("Show Inferred Properties"),
inferred_property_toggle,
space::horizontal(),
text("Show Read Only Triples"),
read_only_toggle
@@ -847,9 +919,9 @@ impl Publisher {
address_input,
save_button,
],
scrollable(body),
space::vertical(),
footer,
scrollable(body),
scrollable(inference_table),
])
.on_back_click(Message::NavigateBack)
.on_forward_click(Message::NavigateForward);
+9 -7
View File
@@ -15,20 +15,19 @@ use ldp::reqwest::{Client, Url};
use ldp::reqwest_middleware::ClientBuilder;
use ldp::traverse::Traverse;
use oxigraph::io::{RdfFormat, RdfParser, RdfSerializer};
use oxigraph::model::{Dataset, Graph, GraphName, GraphNameRef, NamedNode, Quad, Triple};
use oxigraph::model::{Dataset, GraphName, NamedNode, Quad, Triple};
use oxigraph::sparql::{QueryResults, SparqlEvaluator};
use oxigraph::sparql::results::{QueryResultsFormat, QueryResultsSerializer};
use oxigraph::store::Store;
use tracing::{debug, debug_span, error, field};
use tracing::{debug_span, error, field};
use crate::app::Publisher;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{EnvFilter, fmt};
use tracing_subscriber::fmt::format::FmtSpan;
use gl_graph::inference::InferenceEngine;
use gl_search::{Document, Schema, SearchIndex};
use gl_search::SearchIndex;
use crate::args::{AppArgs, Command};
use crate::rdf::ontology::Ontology;
fn main() -> color_eyre::Result<()> {
let appender = tracing_appender::rolling::never("/tmp", "publisher-log");
@@ -38,9 +37,6 @@ fn main() -> color_eyre::Result<()> {
.init();
color_eyre::install()?;
let store = Store::open("/home/alex/.local/share/org.graphofliberty.desktop/ontology")?;
let inference_engine = InferenceEngine::new(store.clone());
let mut index = SearchIndex::builder()
.with_path("/home/alex/.local/share/org.graphofliberty.desktop/index")
.build()
@@ -49,6 +45,9 @@ fn main() -> color_eyre::Result<()> {
let args = AppArgs::parse();
match args.command {
Some(Command::Query(args)) => {
let store = Store::open("/home/alex/.local/share/org.graphofliberty.desktop/ontology")?;
let inference_engine = InferenceEngine::new(store.clone());
let graph_name = GraphName::NamedNode(NamedNode::new_unchecked(format!("file://{}", args.dataset_path.to_string_lossy())));
let mut dataset = RdfParser::from_format(RdfFormat::Turtle)
.with_default_graph(graph_name)
@@ -92,6 +91,9 @@ fn main() -> color_eyre::Result<()> {
}*/
}
Some(Command::Reindex) => {
let store = Store::open("/home/alex/.local/share/org.graphofliberty.desktop/ontology")?;
let inference_engine = InferenceEngine::new(store.clone());
let mut writer = index.writer()?;
debug_span!("Clear Index").in_scope(|| {
writer.delete_all_documents()?;
+1 -12
View File
@@ -60,7 +60,6 @@ static PREFIXES: LazyLock<BTreeMap<String, String>> = LazyLock::new(|| {
pub struct OntologyBuilder {
path: Option<PathBuf>,
materialize_inferences: bool,
}
impl OntologyBuilder {
@@ -70,18 +69,9 @@ impl OntologyBuilder {
self
}
pub fn materialize_inferences(mut self) -> Self {
self.materialize_inferences = true;
self
}
pub fn build(self) -> error::Result<Ontology> {
let mut store = if let Some(path) = self.path {
if self.materialize_inferences {
let store = if let Some(path) = self.path {
Store::open(path)
} else {
Store::open_read_only(path)
}
} else {
Store::new()
}?;
@@ -158,7 +148,6 @@ impl Ontology {
pub fn builder() -> OntologyBuilder {
OntologyBuilder {
path: None,
materialize_inferences: false,
}
}