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