.
This commit is contained in:
Generated
+1
@@ -1633,6 +1633,7 @@ dependencies = [
|
|||||||
"oxigraph",
|
"oxigraph",
|
||||||
"oxilangtag",
|
"oxilangtag",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
"spargebra",
|
||||||
"thiserror 2.0.20",
|
"thiserror 2.0.20",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ rand = "0.10"
|
|||||||
rayon = "1.12"
|
rayon = "1.12"
|
||||||
rfd = "0.17"
|
rfd = "0.17"
|
||||||
slotmap = "1.1"
|
slotmap = "1.1"
|
||||||
|
spargebra = "0.4"
|
||||||
subtitler = "2.6"
|
subtitler = "2.6"
|
||||||
tantivy = "0.26"
|
tantivy = "0.26"
|
||||||
tar = "0.4"
|
tar = "0.4"
|
||||||
|
|||||||
@@ -10,5 +10,6 @@ gl-search.workspace = true
|
|||||||
oxigraph.workspace = true
|
oxigraph.workspace = true
|
||||||
oxilangtag.workspace = true
|
oxilangtag.workspace = true
|
||||||
rayon.workspace = true
|
rayon.workspace = true
|
||||||
|
spargebra.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
+74
-1
@@ -1,6 +1,12 @@
|
|||||||
use oxigraph::model::NamedNodeRef;
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use oxigraph::model::{NamedNode, NamedNodeRef};
|
||||||
|
use oxilangtag::LanguageTag;
|
||||||
|
use gl_search::Schema;
|
||||||
|
use crate::language::LanguageCondition;
|
||||||
use crate::vocab;
|
use crate::vocab;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Class {
|
pub enum Class {
|
||||||
RdfProperty = 0,
|
RdfProperty = 0,
|
||||||
RdfsClass = 1,
|
RdfsClass = 1,
|
||||||
@@ -12,7 +18,33 @@ pub enum Class {
|
|||||||
Manifestation = 10007,
|
Manifestation = 10007,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Class {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Class::RdfProperty => f.write_str("RDF Property"),
|
||||||
|
Class::RdfsClass => f.write_str("RDFS Class"),
|
||||||
|
Class::SkosConcept => f.write_str("SKOS Concept"),
|
||||||
|
Class::Work => f.write_str("Work"),
|
||||||
|
Class::Person => f.write_str("Person"),
|
||||||
|
Class::CorporateBody => f.write_str("Corporate Body"),
|
||||||
|
Class::Expression => f.write_str("Expression"),
|
||||||
|
Class::Manifestation => f.write_str("Manifestation"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Class {
|
impl Class {
|
||||||
|
pub const ALL: &[Class] = &[
|
||||||
|
Class::RdfProperty,
|
||||||
|
Class::RdfsClass,
|
||||||
|
Class::SkosConcept,
|
||||||
|
Class::Work,
|
||||||
|
Class::Person,
|
||||||
|
Class::CorporateBody,
|
||||||
|
Class::Expression,
|
||||||
|
Class::Manifestation,
|
||||||
|
];
|
||||||
|
|
||||||
pub fn to_named_node(&self) -> NamedNodeRef<'_> {
|
pub fn to_named_node(&self) -> NamedNodeRef<'_> {
|
||||||
match self {
|
match self {
|
||||||
Class::RdfProperty => vocab::rdf::PROPERTY,
|
Class::RdfProperty => vocab::rdf::PROPERTY,
|
||||||
@@ -40,4 +72,45 @@ impl Class {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fields(&self, language: LanguageTag<&str>) -> Vec<(gl_search::Field, String)> {
|
||||||
|
match self {
|
||||||
|
Class::RdfProperty => vec![
|
||||||
|
(Schema::curie_field(), String::from("CURIE")),
|
||||||
|
(Schema::field("label", Some(language.primary_language())), String::from("Label")),
|
||||||
|
(Schema::field("definition", Some(language.primary_language())), String::from("Definition")),
|
||||||
|
],
|
||||||
|
Class::RdfsClass => vec![
|
||||||
|
(Schema::curie_field(), String::from("CURIE")),
|
||||||
|
(Schema::field("label", Some(language.primary_language())), String::from("Label")),
|
||||||
|
(Schema::field("definition", Some(language.primary_language())), String::from("Definition")),
|
||||||
|
],
|
||||||
|
Class::SkosConcept => vec![
|
||||||
|
(Schema::curie_field(), String::from("CURIE")),
|
||||||
|
(Schema::field("label", Some(language.primary_language())), String::from("Label")),
|
||||||
|
(Schema::field("definition", Some(language.primary_language())), String::from("Definition")),
|
||||||
|
],
|
||||||
|
Class::Work => vec![
|
||||||
|
(Schema::curie_field(), String::from("CURIE")),
|
||||||
|
(Schema::field("title", Some(language.primary_language())), String::from("Title")),
|
||||||
|
],
|
||||||
|
Class::Person => vec![
|
||||||
|
(Schema::curie_field(), String::from("CURIE")),
|
||||||
|
(Schema::field("given_name", Some(language.primary_language())), String::from("Given Name")),
|
||||||
|
(Schema::field("surname", Some(language.primary_language())), String::from("Surname")),
|
||||||
|
],
|
||||||
|
Class::CorporateBody => vec![
|
||||||
|
(Schema::curie_field(), String::from("CURIE")),
|
||||||
|
(Schema::field("corporate_body", Some(language.primary_language())), String::from("Corporate Body")),
|
||||||
|
],
|
||||||
|
Class::Expression => vec![
|
||||||
|
(Schema::curie_field(), String::from("CURIE")),
|
||||||
|
(Schema::field("title", Some(language.primary_language())), String::from("Title")),
|
||||||
|
],
|
||||||
|
Class::Manifestation => vec![
|
||||||
|
(Schema::curie_field(), String::from("CURIE")),
|
||||||
|
(Schema::field("title", Some(language.primary_language())), String::from("Title")),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,9 @@ pub type Result<R> = std::result::Result<R, Error>;
|
|||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
IriParse(#[from] oxigraph::model::IriParseError),
|
IriParse(#[from] oxigraph::model::IriParseError),
|
||||||
|
|
||||||
@@ -22,6 +25,9 @@ pub enum Error {
|
|||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
UpdateEvaluation(#[from] oxigraph::sparql::UpdateEvaluationError),
|
UpdateEvaluation(#[from] oxigraph::sparql::UpdateEvaluationError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
TonicTransport(#[from] gl_inference::tonic::transport::Error),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
TonicStatus(#[from] gl_inference::tonic::Status),
|
TonicStatus(#[from] gl_inference::tonic::Status),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use oxigraph::model::{NamedNode, NamedNodeRef, Term, TermRef};
|
use oxigraph::model::{NamedNode, Term, TermRef};
|
||||||
use oxigraph::model::vocab::xsd;
|
use oxigraph::model::vocab::xsd;
|
||||||
use crate::vocab::rdf;
|
use crate::vocab::rdf;
|
||||||
|
|
||||||
|
|||||||
+10
-1
@@ -74,6 +74,15 @@ impl<'a> Indexer<'a> {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn manifestation(&self, graph: &Graph, subject: NamedNodeRef) -> HashMap<Field, OwnedValue> {
|
||||||
|
HashMap::from_iter([
|
||||||
|
(
|
||||||
|
Schema::field("title", self.language.primary_language()),
|
||||||
|
self.extract_string(graph, subject, vocab::rdamd::TITLE_OF_MANIFESTATION),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
pub fn graph(&self, graph: &Graph) -> crate::Result<Vec<HashMap<Field, OwnedValue>>> {
|
pub fn graph(&self, graph: &Graph) -> crate::Result<Vec<HashMap<Field, OwnedValue>>> {
|
||||||
let mut entities_and_types = HashSet::new();
|
let mut entities_and_types = HashSet::new();
|
||||||
let triples = graph.triples_for_predicate(vocab::rdf::TYPE);
|
let triples = graph.triples_for_predicate(vocab::rdf::TYPE);
|
||||||
@@ -95,7 +104,7 @@ impl<'a> Indexer<'a> {
|
|||||||
Class::Person => Some(self.person(graph, *subject)),
|
Class::Person => Some(self.person(graph, *subject)),
|
||||||
Class::CorporateBody => Some(self.corporate_body(graph, *subject)),
|
Class::CorporateBody => Some(self.corporate_body(graph, *subject)),
|
||||||
Class::Expression => Some(self.expression(graph, *subject)),
|
Class::Expression => Some(self.expression(graph, *subject)),
|
||||||
Class::Manifestation => Some(self.person(graph, *subject)),
|
Class::Manifestation => Some(self.manifestation(graph, *subject)),
|
||||||
}.map(|mut document| {
|
}.map(|mut document| {
|
||||||
document.insert(Schema::discriminant_field(), OwnedValue::from(class as u64));
|
document.insert(Schema::discriminant_field(), OwnedValue::from(class as u64));
|
||||||
document.insert(Schema::iri_field(), OwnedValue::Str(subject.as_str().to_string()));
|
document.insert(Schema::iri_field(), OwnedValue::Str(subject.as_str().to_string()));
|
||||||
|
|||||||
+50
-3
@@ -1,6 +1,9 @@
|
|||||||
use oxigraph::model::TermRef;
|
use oxigraph::model::{Literal, TermRef};
|
||||||
use oxilangtag::LanguageTag;
|
use oxilangtag::LanguageTag;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
use spargebra::algebra::{Expression, Function, GraphPattern};
|
||||||
|
use spargebra::term::{NamedNode, NamedNodePattern, TermPattern, TriplePattern, Variable};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
pub const ENGLISH_PRIMARY: &str = "en";
|
pub const ENGLISH_PRIMARY: &str = "en";
|
||||||
|
|
||||||
@@ -10,7 +13,7 @@ pub static ENGLISH_OR_UNTAGGED: LazyLock<LanguageCondition> = LazyLock::new(|| {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum LanguageCondition {
|
pub enum LanguageCondition {
|
||||||
ExactMatchOnly(LanguageTag<String>),
|
ExactMatchOnly(LanguageTag<String>),
|
||||||
ExactMatchOrUntagged(LanguageTag<String>),
|
ExactMatchOrUntagged(LanguageTag<String>),
|
||||||
@@ -49,7 +52,51 @@ impl LanguageCondition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_filter_expression(&self, var: &str) -> String {
|
pub fn filter(conditions: impl IntoIterator<Item = (String, Self)>) -> String {
|
||||||
|
let exact_expression = |language, variable| Expression::FunctionCall(
|
||||||
|
Function::LangMatches, vec![
|
||||||
|
Expression::FunctionCall(Function::Lang, vec![
|
||||||
|
Expression::Variable(Variable::new_unchecked(variable))
|
||||||
|
]),
|
||||||
|
Expression::Literal(Literal::new_simple_literal(language))
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
let untagged_expression = |variable| Expression::Not(
|
||||||
|
Box::new(Expression::FunctionCall(Function::HasLang, vec![
|
||||||
|
Expression::Variable(Variable::new_unchecked(variable))
|
||||||
|
]))
|
||||||
|
);
|
||||||
|
|
||||||
|
let condition_to_expression = |variable, condition| match condition {
|
||||||
|
LanguageCondition::ExactMatchOnly(language) =>
|
||||||
|
Some(exact_expression(language.to_string(), variable)),
|
||||||
|
LanguageCondition::ExactMatchOrUntagged(language) => {
|
||||||
|
Some(Expression::Or(
|
||||||
|
Box::new(exact_expression(language.to_string(), variable.clone())),
|
||||||
|
Box::new(untagged_expression(variable)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
LanguageCondition::UntaggedOnly => Some(untagged_expression(variable)),
|
||||||
|
LanguageCondition::AnyOrNone => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut iter = conditions.into_iter();
|
||||||
|
let first_expression = iter.next()
|
||||||
|
.and_then(|(variable, condition)| condition_to_expression(variable, condition));
|
||||||
|
let remaining_expressions = iter.filter_map(|(variable, condition)| condition_to_expression(variable, condition))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let concatenated_expressions = if let Some(expression) = first_expression {
|
||||||
|
if remaining_expressions.is_empty() {
|
||||||
|
//Expression::
|
||||||
|
}
|
||||||
|
} else { String::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_sparql_expression(&self, var: &str) -> String {
|
||||||
|
let foo =
|
||||||
|
debug!("Test: {foo}");
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
LanguageCondition::ExactMatchOnly(language) => {
|
LanguageCondition::ExactMatchOnly(language) => {
|
||||||
format!(r#"FILTER(langMATCHES(LANG(?{var}), "{language}"))"#)
|
format!(r#"FILTER(langMATCHES(LANG(?{var}), "{language}"))"#)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ pub mod category;
|
|||||||
pub mod language;
|
pub mod language;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
pub mod indexer;
|
pub mod indexer;
|
||||||
|
pub mod ontology;
|
||||||
|
|
||||||
pub use curie::{CurieHelper, PREFIXES};
|
pub use curie::{CurieHelper, PREFIXES};
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use oxigraph::io::{RdfFormat, RdfParser, RdfSerializer};
|
||||||
|
use oxigraph::model::{Graph, NamedNode, Triple, TripleRef};
|
||||||
|
use oxigraph::sparql::results::{QueryResultsFormat, QueryResultsParser, SliceQueryResultsParserOutput};
|
||||||
|
use gl_inference::proto::ontology_client::OntologyClient;
|
||||||
|
use gl_inference::proto::OntologyQueryRequest;
|
||||||
|
use gl_inference::tonic::codegen::StdError;
|
||||||
|
use gl_inference::tonic::transport::{Channel, Endpoint};
|
||||||
|
use crate::helpers::{term_as_str, term_to_named_node};
|
||||||
|
use crate::language::LanguageCondition;
|
||||||
|
use crate::vocab::{rdf, rdfs};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct ResourceDescription {
|
||||||
|
pub label: Option<String>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub enum ReadOnlyEntity {
|
||||||
|
Property(NamedNode),
|
||||||
|
Class(NamedNode),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Ontology {
|
||||||
|
client: OntologyClient<Channel>,
|
||||||
|
language: LanguageCondition,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Ontology {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str("ontology")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ontology {
|
||||||
|
pub async fn new<D>(endpoint: D, language: LanguageCondition) -> crate::Result<Self>
|
||||||
|
where
|
||||||
|
D: TryInto<Endpoint>,
|
||||||
|
D::Error: Into<StdError>,
|
||||||
|
{
|
||||||
|
let client = OntologyClient::connect(endpoint).await?;
|
||||||
|
Ok(Self {
|
||||||
|
client,
|
||||||
|
language,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_inference(&mut self, graph: &Graph) -> crate::Result<Graph> {
|
||||||
|
let mut output_buffer = Vec::new();
|
||||||
|
let mut serializer = RdfSerializer::from_format(RdfFormat::Turtle)
|
||||||
|
.for_writer(output_buffer);
|
||||||
|
for triple in graph {
|
||||||
|
serializer.serialize_triple(triple)?;
|
||||||
|
}
|
||||||
|
output_buffer = serializer.finish()?;
|
||||||
|
let turtle = String::from_utf8_lossy(&output_buffer).to_string();
|
||||||
|
|
||||||
|
let mut request = OntologyQueryRequest::default();
|
||||||
|
request.sparql_query = None;
|
||||||
|
request.turtle = Some(turtle);
|
||||||
|
request.base = None;
|
||||||
|
request.inferences_only = true;
|
||||||
|
|
||||||
|
let response = self.client.query(request).await?;
|
||||||
|
Ok(RdfParser::from_format(RdfFormat::Turtle)
|
||||||
|
.for_slice(&response.get_ref().results)
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.map(Triple::from)
|
||||||
|
.collect::<Graph>())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn list_read_only(&mut self) -> crate::Result<HashSet<ReadOnlyEntity>> {
|
||||||
|
let mut request = OntologyQueryRequest::default();
|
||||||
|
request.sparql_query = Some(format!(r#"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
||||||
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
||||||
|
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
|
||||||
|
PREFIX gl: <https://graphofliberty.org/2026/04/ont/>
|
||||||
|
|
||||||
|
SELECT ?subject ?class WHERE {{
|
||||||
|
VALUES ?class {{ rdf:Property rdfs:Class }}
|
||||||
|
?subject a ?class ;
|
||||||
|
gl:readOnly "true"^^xsd:boolean .
|
||||||
|
}}"#));
|
||||||
|
|
||||||
|
let response = self.client.query(request).await?;
|
||||||
|
let parser_output = QueryResultsParser::from_format(QueryResultsFormat::Json)
|
||||||
|
.for_slice(&response.get_ref().results)?;
|
||||||
|
|
||||||
|
let mut results = HashSet::new();
|
||||||
|
if let SliceQueryResultsParserOutput::Solutions(solutions) = parser_output {
|
||||||
|
for solution in solutions.filter_map(Result::ok) {
|
||||||
|
let subject = solution
|
||||||
|
.get("subject")
|
||||||
|
.and_then(term_to_named_node)
|
||||||
|
.map(|node| node.clone());
|
||||||
|
|
||||||
|
let class = solution
|
||||||
|
.get("class")
|
||||||
|
.and_then(term_to_named_node)
|
||||||
|
.map(|node| node.clone());
|
||||||
|
|
||||||
|
if let Some(subject) = subject &&
|
||||||
|
let Some(class) = class {
|
||||||
|
let entry = match class.as_ref() {
|
||||||
|
rdf::PROPERTY => ReadOnlyEntity::Property(subject),
|
||||||
|
rdfs::CLASS => ReadOnlyEntity::Class(subject),
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
results.insert(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn datatypes(&mut self) -> crate::Result<HashMap<NamedNode, ResourceDescription>> {
|
||||||
|
let label_filter = self.language.to_filter_expression("label");
|
||||||
|
let description_filter = self.language.to_filter_expression("description");
|
||||||
|
|
||||||
|
let mut request = OntologyQueryRequest::default();
|
||||||
|
request.sparql_query = Some(format!(r#"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
||||||
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
||||||
|
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
|
||||||
|
|
||||||
|
SELECT ?subject ?label ?description WHERE {{
|
||||||
|
?subject a rdfs:Datatype
|
||||||
|
OPTIONAL {{ ?subject rdfs:label ?label }}
|
||||||
|
{label_filter}
|
||||||
|
|
||||||
|
OPTIONAL {{ ?subject rdfs:comment ?comment }}
|
||||||
|
OPTIONAL {{ ?subject skos:definition ?definition }}
|
||||||
|
BIND(COALESCE(?definition, ?comment) AS ?description)
|
||||||
|
{description_filter}
|
||||||
|
}}"#));
|
||||||
|
|
||||||
|
let response = self.client.query(request).await?;
|
||||||
|
let parser_output = QueryResultsParser::from_format(QueryResultsFormat::Json)
|
||||||
|
.for_slice(&response.get_ref().results)?;
|
||||||
|
|
||||||
|
let mut results = HashMap::new();
|
||||||
|
if let SliceQueryResultsParserOutput::Solutions(solutions) = parser_output {
|
||||||
|
for solution in solutions.filter_map(Result::ok) {
|
||||||
|
let subject = solution
|
||||||
|
.get("subject")
|
||||||
|
.and_then(term_to_named_node)
|
||||||
|
.map(|node| node.clone());
|
||||||
|
|
||||||
|
if let Some(subject) = subject {
|
||||||
|
let label = solution
|
||||||
|
.get("label")
|
||||||
|
.and_then(term_as_str)
|
||||||
|
.map(String::from);
|
||||||
|
|
||||||
|
let description = solution
|
||||||
|
.get("description")
|
||||||
|
.and_then(term_as_str)
|
||||||
|
.map(String::from);
|
||||||
|
|
||||||
|
results.insert(subject, ResourceDescription { label, description });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn resource_description(&mut self, subject: NamedNode) -> crate::Result<ResourceDescription> {
|
||||||
|
let label_filter = self.language.to_filter_expression("label");
|
||||||
|
let description_filter = self.language.to_filter_expression("description");
|
||||||
|
|
||||||
|
let mut request = OntologyQueryRequest::default();
|
||||||
|
request.sparql_query = Some(format!(r#"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
||||||
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
||||||
|
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
|
||||||
|
|
||||||
|
SELECT ?label ?description WHERE {{
|
||||||
|
OPTIONAL {{ {subject} rdfs:label ?label }}
|
||||||
|
{label_filter}
|
||||||
|
|
||||||
|
OPTIONAL {{ {subject} rdfs:comment ?comment }}
|
||||||
|
OPTIONAL {{ {subject} skos:definition ?definition }}
|
||||||
|
BIND(COALESCE(?definition, ?comment) AS ?description)
|
||||||
|
{description_filter}
|
||||||
|
}}"#));
|
||||||
|
|
||||||
|
let response = self.client.query(request).await?;
|
||||||
|
let parser_output = QueryResultsParser::from_format(QueryResultsFormat::Json)
|
||||||
|
.for_slice(&response.get_ref().results)?;
|
||||||
|
|
||||||
|
if let SliceQueryResultsParserOutput::Solutions(solutions) = parser_output {
|
||||||
|
Ok(solutions.filter_map(Result::ok)
|
||||||
|
.next()
|
||||||
|
.map(|solution| {
|
||||||
|
let label = solution
|
||||||
|
.get("label")
|
||||||
|
.and_then(term_as_str)
|
||||||
|
.map(String::from);
|
||||||
|
|
||||||
|
let description = solution
|
||||||
|
.get("description")
|
||||||
|
.and_then(term_as_str)
|
||||||
|
.map(String::from);
|
||||||
|
|
||||||
|
ResourceDescription {
|
||||||
|
label,
|
||||||
|
description,
|
||||||
|
}
|
||||||
|
}).unwrap_or(ResourceDescription::default())
|
||||||
|
)
|
||||||
|
} else { unreachable!() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,3 +70,10 @@ pub mod rdaed {
|
|||||||
pub const TITLE_OF_EXPRESSION: NamedNodeRef =
|
pub const TITLE_OF_EXPRESSION: NamedNodeRef =
|
||||||
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/e/datatype/P20312");
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/e/datatype/P20312");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod rdamd {
|
||||||
|
use oxigraph::model::NamedNodeRef;
|
||||||
|
|
||||||
|
pub const TITLE_OF_MANIFESTATION: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/m/datatype/P30335");
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ 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 tonic::{Request, Response, Status};
|
use tonic::{Request, Response, Status};
|
||||||
use tracing::{debug_span, field};
|
use tracing::{debug, debug_span, field};
|
||||||
use gl_inference::proto::ontology_server::Ontology;
|
use gl_inference::proto::ontology_server::Ontology;
|
||||||
use gl_inference::proto::{OntologyClearResponse, OntologyLoadRequest, OntologyLoadResponse, OntologyQueryRequest, OntologyQueryResponse};
|
use gl_inference::proto::{OntologyClearResponse, OntologyLoadRequest, OntologyLoadResponse, OntologyQueryRequest, OntologyQueryResponse};
|
||||||
|
|
||||||
@@ -96,10 +96,11 @@ impl Ontology for OntologyService {
|
|||||||
let request = request.get_ref();
|
let request = request.get_ref();
|
||||||
let mut response = OntologyQueryResponse::default();
|
let mut response = OntologyQueryResponse::default();
|
||||||
|
|
||||||
|
let provided_dataset;
|
||||||
let graph_name = if let Some(turtle) = &request.turtle {
|
let graph_name = if let Some(turtle) = &request.turtle {
|
||||||
let random_graph_identifier = BlankNode::default();
|
let random_graph_identifier = BlankNode::default();
|
||||||
let graph_name = GraphName::NamedNode(NamedNode::new_unchecked(format!("https://graphofliberty.org/inference/{}", random_graph_identifier.as_str())));
|
let graph_name = GraphName::NamedNode(NamedNode::new_unchecked(format!("https://graphofliberty.org/inference/{}", random_graph_identifier.as_str())));
|
||||||
let dataset = RdfParser::from_format(RdfFormat::Turtle)
|
provided_dataset = RdfParser::from_format(RdfFormat::Turtle)
|
||||||
.for_slice(&turtle)
|
.for_slice(&turtle)
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.map(|mut quad| {
|
.map(|mut quad| {
|
||||||
@@ -107,7 +108,7 @@ impl Ontology for OntologyService {
|
|||||||
quad
|
quad
|
||||||
}).collect::<Dataset>();
|
}).collect::<Dataset>();
|
||||||
|
|
||||||
self.ontology.extend(&dataset)
|
self.ontology.extend(&provided_dataset)
|
||||||
.map_err(|err| Status::internal(err.to_string()))?;
|
.map_err(|err| Status::internal(err.to_string()))?;
|
||||||
|
|
||||||
let inferences = crate::logic::infer(0, &self.ontology, graph_name.as_ref())
|
let inferences = crate::logic::infer(0, &self.ontology, graph_name.as_ref())
|
||||||
@@ -115,11 +116,14 @@ impl Ontology for OntologyService {
|
|||||||
span.record("inferences", inferences);
|
span.record("inferences", inferences);
|
||||||
graph_name
|
graph_name
|
||||||
} else {
|
} else {
|
||||||
|
provided_dataset = Dataset::new();
|
||||||
ONTOLOGY_GRAPH.into_owned()
|
ONTOLOGY_GRAPH.into_owned()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut output_buffer = Vec::new();
|
let mut output_buffer = Vec::new();
|
||||||
if let Some(sparql_query) = &request.sparql_query {
|
if let Some(sparql_query) = &request.sparql_query {
|
||||||
|
debug!("SPARQL Query: {sparql_query}");
|
||||||
|
|
||||||
let mut evaluator = SparqlEvaluator::new();
|
let mut evaluator = SparqlEvaluator::new();
|
||||||
for (name, iri) in &request.prefixes {
|
for (name, iri) in &request.prefixes {
|
||||||
evaluator = evaluator.with_prefix(name, iri)
|
evaluator = evaluator.with_prefix(name, iri)
|
||||||
@@ -185,11 +189,13 @@ impl Ontology for OntologyService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut serializer = serializer.for_writer(output_buffer);
|
let mut serializer = serializer.for_writer(output_buffer);
|
||||||
let graph = self.ontology
|
let resulting_dataset = self.ontology
|
||||||
.quads_for_pattern(None, None, None, Some(graph_name.as_ref()))
|
.quads_for_pattern(None, None, None, Some(graph_name.as_ref()))
|
||||||
.filter_map(Result::ok);
|
.filter_map(Result::ok);
|
||||||
for triple in graph {
|
for quad in resulting_dataset {
|
||||||
serializer.serialize_triple(triple.as_ref())?;
|
if !(request.inferences_only && provided_dataset.contains(quad.as_ref())) {
|
||||||
|
serializer.serialize_triple(quad.as_ref())?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
output_buffer = serializer.finish()?;
|
output_buffer = serializer.finish()?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ message OntologyQueryRequest {
|
|||||||
optional string sparql_query = 2;
|
optional string sparql_query = 2;
|
||||||
map<string, string> prefixes = 3;
|
map<string, string> prefixes = 3;
|
||||||
optional string base = 4;
|
optional string base = 4;
|
||||||
|
bool inferences_only = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message OntologyQueryResponse {
|
message OntologyQueryResponse {
|
||||||
|
|||||||
+177
-152
@@ -1,22 +1,21 @@
|
|||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
use crate::navigator::Navigator;
|
use crate::navigator::Navigator;
|
||||||
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::CurieHelper;
|
use gl_graph::CurieHelper;
|
||||||
use gl_graph::language;
|
|
||||||
use gl_search::{Schema, SearchIndex};
|
use gl_search::{Schema, SearchIndex};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use iced::alignment::Horizontal;
|
use iced::alignment::Horizontal;
|
||||||
use iced::keyboard::{Event, key};
|
use iced::keyboard::{Event, key};
|
||||||
use iced::widget::button::Style;
|
use iced::widget::button::Style;
|
||||||
use iced::widget::grid::Sizing;
|
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
button, center, column, combo_box, container, grid, mouse_area, opaque, operation, pick_list,
|
button, center, column, combo_box, container, grid, mouse_area, opaque, operation, pick_list,
|
||||||
row, scrollable, space, stack, table, text, text_input, toggler,
|
row, scrollable, space, stack, table, text, text_input, toggler,
|
||||||
};
|
};
|
||||||
use iced::window::Settings;
|
use iced::window::Settings;
|
||||||
use iced::{Background, Color, Element, Length, Subscription, Task, color, keyboard, window};
|
use iced::{Background, Color, Element, Length, Subscription, Task, color, keyboard, window};
|
||||||
|
use iced::advanced::text::Wrapping;
|
||||||
use ldp::middleware::BasicAuthMiddleware;
|
use ldp::middleware::BasicAuthMiddleware;
|
||||||
use ldp::model::{KeyedDataset, QuadKey};
|
use ldp::model::{KeyedDataset, QuadKey};
|
||||||
use ldp::reqwest::{Client, Url};
|
use ldp::reqwest::{Client, Url};
|
||||||
@@ -24,15 +23,23 @@ 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::{
|
use oxigraph::model::{BaseDirection, Dataset, Graph, NamedNode, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, Term, TermRef, Triple, TripleRef};
|
||||||
BaseDirection, Dataset, Graph, NamedNode, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, Term,
|
use tracing::{debug, error, trace};
|
||||||
TripleRef,
|
use gl_graph::class::Class;
|
||||||
};
|
use gl_graph::ontology::{Ontology, ReadOnlyEntity, ResourceDescription};
|
||||||
use tracing::{debug_span, error, trace};
|
use gl_search::tantivy::schema::Value;
|
||||||
|
use crate::tasks;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) enum Message {
|
pub(crate) enum Message {
|
||||||
None,
|
None,
|
||||||
|
ConnectToOntologyService(String),
|
||||||
|
ConnectedToOntologyService(Ontology),
|
||||||
|
CacheDatatypes(HashMap<NamedNode, ResourceDescription>),
|
||||||
|
CacheReadOnlyEntities(HashSet<ReadOnlyEntity>),
|
||||||
|
PopulateCaches,
|
||||||
|
LookupResourceDescription(NamedNode),
|
||||||
|
CacheResourceDescription(NamedNode, ResourceDescription),
|
||||||
WindowClosed(window::Id),
|
WindowClosed(window::Id),
|
||||||
URLInputChanged(String),
|
URLInputChanged(String),
|
||||||
URLInputSubmitted,
|
URLInputSubmitted,
|
||||||
@@ -49,8 +56,8 @@ pub(crate) enum Message {
|
|||||||
HoverRow(QuadKey),
|
HoverRow(QuadKey),
|
||||||
UnhoverRow(QuadKey),
|
UnhoverRow(QuadKey),
|
||||||
QueryUpdated(String),
|
QueryUpdated(String),
|
||||||
SetSearchResults(Vec<()>),
|
SetSearchResults(Vec<HashMap<gl_search::Field, gl_search::OwnedValue>>),
|
||||||
QueryTypeUpdated(LabeledIri),
|
UpdateQueryClass(Class),
|
||||||
SearchResultClicked(NamedNode),
|
SearchResultClicked(NamedNode),
|
||||||
DatatypeUpdated(QuadKey, Option<String>),
|
DatatypeUpdated(QuadKey, Option<String>),
|
||||||
LanguageUpdated(QuadKey, Option<String>),
|
LanguageUpdated(QuadKey, Option<String>),
|
||||||
@@ -68,11 +75,10 @@ pub(crate) enum Message {
|
|||||||
NavigateBack,
|
NavigateBack,
|
||||||
NavigateForward,
|
NavigateForward,
|
||||||
ResetState,
|
ResetState,
|
||||||
SetReadOnly(bool),
|
SetShowReadOnly(bool),
|
||||||
SetInferredTypes(bool),
|
SetShowInferredTriples(bool),
|
||||||
SetInferredProperties(bool),
|
|
||||||
RunInference,
|
RunInference,
|
||||||
SetInferredTriples(Dataset),
|
SetInferredTriples(Graph),
|
||||||
Event(Event),
|
Event(Event),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,16 +98,18 @@ pub(crate) enum SearchResultClickAction {
|
|||||||
struct SearchState {
|
struct SearchState {
|
||||||
window_id: window::Id,
|
window_id: window::Id,
|
||||||
action: SearchResultClickAction,
|
action: SearchResultClickAction,
|
||||||
entity_selection: Option<LabeledIri>,
|
entity_class_selection: Option<Class>,
|
||||||
query: String,
|
query: String,
|
||||||
results: Vec<()>,
|
results: Vec<HashMap<gl_search::Field, gl_search::OwnedValue>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Publisher {
|
pub(crate) struct Publisher {
|
||||||
http_client: ClientWithMiddleware,
|
http_client: ClientWithMiddleware,
|
||||||
curie_helper: CurieHelper,
|
curie_helper: CurieHelper,
|
||||||
ontology: Ontology,
|
ontology: Option<Ontology>,
|
||||||
|
read_only_entities: HashSet<ReadOnlyEntity>,
|
||||||
abbreviated_datatypes: Vec<String>,
|
abbreviated_datatypes: Vec<String>,
|
||||||
|
resource_descriptions: HashMap<NamedNode, ResourceDescription>,
|
||||||
window_id: window::Id,
|
window_id: window::Id,
|
||||||
url_input: String,
|
url_input: String,
|
||||||
url_input_valid: bool,
|
url_input_valid: bool,
|
||||||
@@ -109,44 +117,34 @@ pub(crate) struct Publisher {
|
|||||||
document: RdfSource<KeyedDataset<RowState>>,
|
document: RdfSource<KeyedDataset<RowState>>,
|
||||||
inferred_triples: Graph,
|
inferred_triples: Graph,
|
||||||
show_read_only: bool,
|
show_read_only: bool,
|
||||||
show_inferred_types: bool,
|
show_inferred_triples: 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,
|
||||||
traversal: Option<Dataset>,
|
|
||||||
show_overwrite_confirmation: bool,
|
show_overwrite_confirmation: bool,
|
||||||
modified: bool,
|
modified: bool,
|
||||||
show_new_document_buttons: bool,
|
show_new_document_buttons: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Publisher {
|
impl Publisher {
|
||||||
pub(crate) fn new() -> (Self, Task<Message>) {
|
fn is_read_only<'a>(&'a self, triple: impl Into<TripleRef<'a>>) -> bool {
|
||||||
let curie_helper = CurieHelper::new(Ontology::prefixes().clone());
|
let triple = triple.into();
|
||||||
|
if triple.predicate == rdf::TYPE &&
|
||||||
|
let TermRef::NamedNode(node) = triple.object {
|
||||||
|
self.read_only_entities.contains(&ReadOnlyEntity::Class(node.into_owned()))
|
||||||
|
} else {
|
||||||
|
self.read_only_entities.contains(&ReadOnlyEntity::Property(triple.predicate.into_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let ontology = debug_span!("Ontology Creation").in_scope(|| {
|
pub(crate) fn new() -> (Self, Task<Message>) {
|
||||||
Ontology::builder()
|
let curie_helper = CurieHelper::new(gl_graph::PREFIXES.clone());
|
||||||
.with_path("/home/alex/.local/share/org.graphofliberty.desktop/ontology")
|
|
||||||
.build()
|
|
||||||
.expect("Failed to build ontology")
|
|
||||||
});
|
|
||||||
|
|
||||||
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()
|
||||||
.expect("Failed to build search index");
|
.expect("Failed to build search index");
|
||||||
|
|
||||||
let mut abbreviated_datatypes = ontology
|
|
||||||
.datatypes()
|
|
||||||
.into_iter()
|
|
||||||
.map(|node| {
|
|
||||||
curie_helper
|
|
||||||
.abbreviate(None, node.as_str())
|
|
||||||
.unwrap_or(node.as_str().to_string())
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
abbreviated_datatypes.sort();
|
|
||||||
|
|
||||||
let (id, task) = window::open(Settings::default());
|
let (id, task) = window::open(Settings::default());
|
||||||
|
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
@@ -165,8 +163,10 @@ impl Publisher {
|
|||||||
Self {
|
Self {
|
||||||
http_client,
|
http_client,
|
||||||
curie_helper,
|
curie_helper,
|
||||||
ontology,
|
ontology: None,
|
||||||
abbreviated_datatypes,
|
read_only_entities: HashSet::new(),
|
||||||
|
abbreviated_datatypes: Vec::new(),
|
||||||
|
resource_descriptions: HashMap::new(),
|
||||||
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,
|
||||||
@@ -174,17 +174,15 @@ impl Publisher {
|
|||||||
document,
|
document,
|
||||||
inferred_triples: Graph::new(),
|
inferred_triples: Graph::new(),
|
||||||
show_read_only: false,
|
show_read_only: false,
|
||||||
show_inferred_types: false,
|
show_inferred_triples: false,
|
||||||
show_inferred_properties: false,
|
|
||||||
hovered_row: None,
|
hovered_row: None,
|
||||||
search_state: None,
|
search_state: None,
|
||||||
index,
|
index,
|
||||||
traversal: None,
|
|
||||||
show_overwrite_confirmation: false,
|
show_overwrite_confirmation: false,
|
||||||
modified: false,
|
modified: false,
|
||||||
show_new_document_buttons: false,
|
show_new_document_buttons: false,
|
||||||
},
|
},
|
||||||
task.map(|_| Message::None),
|
task.map(|_| Message::ConnectToOntologyService("http://[::1]:3000".to_string())),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +191,39 @@ impl Publisher {
|
|||||||
trace!(?message);
|
trace!(?message);
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
|
Message::ConnectToOntologyService(endpoint) => {
|
||||||
|
task = tasks::connect_to_ontology_service(endpoint)
|
||||||
|
.chain(Task::done(Message::PopulateCaches));
|
||||||
|
}
|
||||||
|
Message::ConnectedToOntologyService(ontology) => {
|
||||||
|
self.ontology = Some(ontology);
|
||||||
|
}
|
||||||
|
Message::PopulateCaches => {
|
||||||
|
if let Some(ontology) = &mut self.ontology {
|
||||||
|
task = Task::batch([
|
||||||
|
tasks::list_datatypes(ontology.clone()),
|
||||||
|
tasks::list_read_only_entities(ontology.clone()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::CacheDatatypes(datatypes) => {
|
||||||
|
self.abbreviated_datatypes = datatypes.keys()
|
||||||
|
.map(|node| {
|
||||||
|
self.curie_helper.abbreviate(None, node.as_str())
|
||||||
|
.unwrap_or(node.as_str().to_string())
|
||||||
|
}).collect();
|
||||||
|
}
|
||||||
|
Message::CacheReadOnlyEntities(entities) => {
|
||||||
|
self.read_only_entities = entities;
|
||||||
|
}
|
||||||
|
Message::LookupResourceDescription(node) => {
|
||||||
|
if let Some(ontology) = &mut self.ontology {
|
||||||
|
task = tasks::lookup_resource_description(ontology.clone(), node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::CacheResourceDescription(node, description) => {
|
||||||
|
self.resource_descriptions.insert(node, description);
|
||||||
|
}
|
||||||
Message::WindowClosed(id) => {
|
Message::WindowClosed(id) => {
|
||||||
if self.window_id == id {
|
if self.window_id == id {
|
||||||
task = iced::exit();
|
task = iced::exit();
|
||||||
@@ -248,16 +279,16 @@ impl Publisher {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
Message::LoadDocument(document) => {
|
Message::LoadDocument(document) => {
|
||||||
let messages = document.dataset().quads.iter().map(|(key, quad)| {
|
let add_row_tasks = document.dataset().quads.iter().map(|(key, quad)| {
|
||||||
let datatype_state = combo_box::State::new(self.abbreviated_datatypes.clone());
|
let datatype_state = combo_box::State::new(self.abbreviated_datatypes.clone());
|
||||||
|
|
||||||
let state = RowState {
|
let state = RowState {
|
||||||
read_only: self.ontology.is_read_only(quad.as_ref()),
|
read_only: self.is_read_only(quad.as_ref()),
|
||||||
datatype_state,
|
datatype_state,
|
||||||
};
|
};
|
||||||
Message::AddRow(Some((key, state)))
|
Task::done(Message::AddRow(Some((key, state))))
|
||||||
|
.chain(Task::done(Message::LookupResourceDescription(quad.predicate.clone())))
|
||||||
});
|
});
|
||||||
task = Task::batch(messages.map(Task::done))
|
task = Task::batch(add_row_tasks)
|
||||||
.chain(Task::done(Message::RunInference))
|
.chain(Task::done(Message::RunInference))
|
||||||
.chain(Task::done(Message::ResetState));
|
.chain(Task::done(Message::ResetState));
|
||||||
|
|
||||||
@@ -318,33 +349,29 @@ impl Publisher {
|
|||||||
self.search_state = Some(SearchState {
|
self.search_state = Some(SearchState {
|
||||||
window_id: id,
|
window_id: id,
|
||||||
action,
|
action,
|
||||||
entity_selection: None,
|
entity_class_selection: None,
|
||||||
query: String::new(),
|
query: String::new(),
|
||||||
results: Vec::new(),
|
results: Vec::new(),
|
||||||
});
|
});
|
||||||
task = window_task.then(|_| operation::focus("query"));
|
task = window_task.then(|_| operation::focus("query"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(selected_entity_class) = selected_entity_class {
|
if let Some(selected_entity_class) = selected_entity_class &&
|
||||||
let selection = self.ontology.info(
|
let Some(class) = Class::try_from_named_node(selected_entity_class) {
|
||||||
&selected_entity_class.into_owned(),
|
task = task.chain(Task::done(Message::UpdateQueryClass(class)))
|
||||||
&*language::ENGLISH_OR_UNTAGGED,
|
|
||||||
);
|
|
||||||
task = task.chain(Task::done(Message::QueryTypeUpdated(selection)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::QueryTypeUpdated(type_) => {
|
Message::UpdateQueryClass(type_) => {
|
||||||
if let Some(search_state) = &mut self.search_state {
|
if let Some(search_state) = &mut self.search_state {
|
||||||
search_state.entity_selection = Some(type_);
|
search_state.entity_class_selection = Some(type_);
|
||||||
task = Task::done(Message::QueryUpdated(search_state.query.clone()));
|
task = Task::done(Message::QueryUpdated(search_state.query.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::QueryUpdated(new_query) => {
|
Message::QueryUpdated(new_query) => {
|
||||||
if let Some(search_state) = &mut self.search_state {
|
if let Some(search_state) = &mut self.search_state {
|
||||||
let category_id = search_state
|
let category_id = search_state.entity_class_selection
|
||||||
.entity_selection
|
.as_ref()
|
||||||
.clone()
|
.map(|selection| selection.to_owned() as u64);
|
||||||
.and_then(|info| self.ontology.category_id(&info.iri));
|
|
||||||
|
|
||||||
let query = new_query.clone();
|
let query = new_query.clone();
|
||||||
search_state.query = new_query;
|
search_state.query = new_query;
|
||||||
@@ -352,7 +379,7 @@ impl Publisher {
|
|||||||
.index
|
.index
|
||||||
.query(category_id, query.as_str(), Schema::all_fields(), 25)
|
.query(category_id, query.as_str(), Schema::all_fields(), 25)
|
||||||
.expect("Unable to complete search");
|
.expect("Unable to complete search");
|
||||||
task = Task::done(Message::SetSearchResults(vec![]));
|
task = Task::done(Message::SetSearchResults(results));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Message::SetSearchResults(results) => {
|
Message::SetSearchResults(results) => {
|
||||||
@@ -449,8 +476,16 @@ impl Publisher {
|
|||||||
}
|
}
|
||||||
Message::SaveGraph(overwrite) => {
|
Message::SaveGraph(overwrite) => {
|
||||||
let client = self.http_client.clone();
|
let client = self.http_client.clone();
|
||||||
|
let read_only_entities = self.read_only_entities.clone();
|
||||||
let options = SerializationOptions::from_format(RdfFormat::Turtle)
|
let options = SerializationOptions::from_format(RdfFormat::Turtle)
|
||||||
.with_filter(self.ontology.exclude_read_only());
|
.with_filter(move |triple| {
|
||||||
|
if triple.predicate == rdf::TYPE &&
|
||||||
|
let TermRef::NamedNode(node) = triple.object {
|
||||||
|
read_only_entities.contains(&ReadOnlyEntity::Class(node.into_owned()))
|
||||||
|
} else {
|
||||||
|
read_only_entities.contains(&ReadOnlyEntity::Property(triple.predicate.into_owned()))
|
||||||
|
}
|
||||||
|
});
|
||||||
let request = self
|
let request = self
|
||||||
.document
|
.document
|
||||||
.to_update(options)
|
.to_update(options)
|
||||||
@@ -538,19 +573,33 @@ impl Publisher {
|
|||||||
task = Task::done(Message::URLInputChanged(url.to_string()))
|
task = Task::done(Message::URLInputChanged(url.to_string()))
|
||||||
.chain(Task::done(Message::NavigateTo(url)));
|
.chain(Task::done(Message::NavigateTo(url)));
|
||||||
}
|
}
|
||||||
Message::SetReadOnly(value) => {
|
Message::SetShowReadOnly(value) => {
|
||||||
self.show_read_only = value;
|
self.show_read_only = value;
|
||||||
}
|
}
|
||||||
Message::SetInferredTypes(value) => {
|
Message::SetShowInferredTriples(value) => {
|
||||||
self.show_inferred_types = value;
|
self.show_inferred_triples = value;
|
||||||
}
|
|
||||||
Message::SetInferredProperties(value) => {
|
|
||||||
self.show_inferred_properties = value;
|
|
||||||
}
|
}
|
||||||
Message::RunInference => {
|
Message::RunInference => {
|
||||||
|
if let Some(ontology) = &mut self.ontology {
|
||||||
|
let graph = self.document
|
||||||
|
.dataset()
|
||||||
|
.quads
|
||||||
|
.iter()
|
||||||
|
.map(|(_, quad)| Triple::from(quad.clone()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
task = tasks::run_inference(ontology.clone(), graph);
|
||||||
}
|
}
|
||||||
Message::SetInferredTriples(dataset) => {
|
}
|
||||||
self.inferred_triples = Graph::from_iter(&dataset);
|
Message::SetInferredTriples(graph) => {
|
||||||
|
let messages = graph.iter()
|
||||||
|
.map(|triple| {
|
||||||
|
Message::LookupResourceDescription(triple.predicate.into_owned())
|
||||||
|
}).map(Task::done)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
task = Task::batch(messages);
|
||||||
|
|
||||||
|
self.inferred_triples = graph;
|
||||||
}
|
}
|
||||||
Message::Event(Event::KeyPressed {
|
Message::Event(Event::KeyPressed {
|
||||||
key: keyboard::Key::Named(key::Named::Tab),
|
key: keyboard::Key::Named(key::Named::Tab),
|
||||||
@@ -582,7 +631,7 @@ impl Publisher {
|
|||||||
fn view_row<'a>(
|
fn view_row<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
key: QuadKey,
|
key: QuadKey,
|
||||||
triple: &'a Quad,
|
quad: &'a Quad,
|
||||||
state: &'a RowState,
|
state: &'a RowState,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
const BUTTON_WIDTH: Length = Length::Fixed(35.0);
|
const BUTTON_WIDTH: Length = Length::Fixed(35.0);
|
||||||
@@ -598,13 +647,13 @@ impl Publisher {
|
|||||||
|
|
||||||
let base = self.document.origin().as_str();
|
let base = self.document.origin().as_str();
|
||||||
|
|
||||||
let subject = match &triple.subject {
|
let subject = match &quad.subject {
|
||||||
NamedOrBlankNode::NamedNode(subject) => subject.as_str(),
|
NamedOrBlankNode::NamedNode(subject) => subject.as_str(),
|
||||||
_ => "",
|
_ => "",
|
||||||
};
|
};
|
||||||
|
|
||||||
let subject_input_base = iri_input(&self.curie_helper, "Subject", Some(&base), subject);
|
let subject_input_base = iri_input(&self.curie_helper, "Subject", Some(&base), subject);
|
||||||
let subject_input = if self.ontology.is_read_only(triple.as_ref()) {
|
let subject_input = if state.read_only {
|
||||||
subject_input_base
|
subject_input_base
|
||||||
} else {
|
} else {
|
||||||
subject_input_base.on_input(move |value| Message::SubjectUpdated(key, value))
|
subject_input_base.on_input(move |value| Message::SubjectUpdated(key, value))
|
||||||
@@ -614,9 +663,9 @@ impl Publisher {
|
|||||||
&self.curie_helper,
|
&self.curie_helper,
|
||||||
"Predicate",
|
"Predicate",
|
||||||
Some(&base),
|
Some(&base),
|
||||||
triple.predicate.as_str(),
|
quad.predicate.as_str(),
|
||||||
);
|
);
|
||||||
let predicate_input = if self.ontology.is_read_only(triple.as_ref()) {
|
let predicate_input = if state.read_only {
|
||||||
predicate_input_base
|
predicate_input_base
|
||||||
} else {
|
} else {
|
||||||
predicate_input_base
|
predicate_input_base
|
||||||
@@ -627,19 +676,17 @@ impl Publisher {
|
|||||||
.on_shift_click(Message::NavigateToPredicate(key))
|
.on_shift_click(Message::NavigateToPredicate(key))
|
||||||
};
|
};
|
||||||
|
|
||||||
let predicate_info = self
|
let predicate_label = self.resource_descriptions
|
||||||
.ontology
|
.get(&quad.predicate)
|
||||||
.info(&triple.predicate, &*language::ENGLISH_OR_UNTAGGED);
|
.and_then(|description| description.label.clone())
|
||||||
let predicate_label = container(text(predicate_info.label));
|
.map(|label| container(text(label)));
|
||||||
|
|
||||||
let term = TermHelper::new(&triple.object);
|
let term = TermHelper::new(&quad.object);
|
||||||
|
|
||||||
let value_label = term.value_as_named_node().and_then(|node| {
|
let value_label = term.value_as_named_node()
|
||||||
let info = self
|
.and_then(|node| self.resource_descriptions.get(&node.into_owned()))
|
||||||
.ontology
|
.and_then(|description| description.label.clone())
|
||||||
.info(&node.into_owned(), &*language::ENGLISH_OR_UNTAGGED);
|
.map(|label| container(text(label)));
|
||||||
Some(container(text(info.label)))
|
|
||||||
});
|
|
||||||
|
|
||||||
let value = term
|
let value = term
|
||||||
.value_as_named_node()
|
.value_as_named_node()
|
||||||
@@ -658,7 +705,7 @@ impl Publisher {
|
|||||||
// text_input as opposed to an iri_input.
|
// text_input as opposed to an iri_input.
|
||||||
let object_input: Element<Message> = if term.datatype().is_some() {
|
let object_input: Element<Message> = if term.datatype().is_some() {
|
||||||
let base = text_input("Object", value.clone());
|
let base = text_input("Object", value.clone());
|
||||||
if self.ontology.is_read_only(triple.as_ref()) {
|
if state.read_only {
|
||||||
base.into()
|
base.into()
|
||||||
} else {
|
} else {
|
||||||
base.on_input(move |value| Message::ObjectUpdated(key, value))
|
base.on_input(move |value| Message::ObjectUpdated(key, value))
|
||||||
@@ -667,7 +714,7 @@ impl Publisher {
|
|||||||
} else {
|
} else {
|
||||||
let base = iri_input(&self.curie_helper, "Object", Some(&base), value.as_str())
|
let base = iri_input(&self.curie_helper, "Object", Some(&base), value.as_str())
|
||||||
.on_shift_click(Message::NavigateToObject(key));
|
.on_shift_click(Message::NavigateToObject(key));
|
||||||
if self.ontology.is_read_only(triple.as_ref()) {
|
if state.read_only {
|
||||||
base.into()
|
base.into()
|
||||||
} else {
|
} else {
|
||||||
base.align_x(value_alignment)
|
base.align_x(value_alignment)
|
||||||
@@ -751,13 +798,13 @@ impl Publisher {
|
|||||||
|
|
||||||
fn view_new_entity_buttons(
|
fn view_new_entity_buttons(
|
||||||
&self,
|
&self,
|
||||||
entities: impl IntoIterator<Item = LabeledIri>,
|
entities: impl IntoIterator<Item = ResourceDescription>,
|
||||||
) -> Element<'_, Message> {
|
) -> Element<'_, Message> {
|
||||||
let buttons = entities.into_iter().map(|entity| {
|
/*let buttons = entities.into_iter().map(|entity| {
|
||||||
let abbreviation = self
|
let abbreviation = self
|
||||||
.curie_helper
|
.curie_helper
|
||||||
.abbreviate(None, entity.iri.as_str())
|
.abbreviate(None, entity.label.as_str())
|
||||||
.unwrap_or_else(|| entity.iri.as_str().to_string());
|
.unwrap_or_else(|| entity.as_str().to_string());
|
||||||
|
|
||||||
let label = format!("{} ({})", entity.label, abbreviation);
|
let label = format!("{} ({})", entity.label, abbreviation);
|
||||||
button(text(label))
|
button(text(label))
|
||||||
@@ -767,7 +814,8 @@ impl Publisher {
|
|||||||
|
|
||||||
grid(buttons)
|
grid(buttons)
|
||||||
.height(Sizing::EvenlyDistribute(Length::Shrink))
|
.height(Sizing::EvenlyDistribute(Length::Shrink))
|
||||||
.into()
|
.into()*/
|
||||||
|
text("to do").into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn view(&self, window: window::Id) -> Element<'_, Message> {
|
pub(crate) fn view(&self, window: window::Id) -> Element<'_, Message> {
|
||||||
@@ -778,44 +826,27 @@ impl Publisher {
|
|||||||
.on_input(Message::QueryUpdated)
|
.on_input(Message::QueryUpdated)
|
||||||
.id("query");
|
.id("query");
|
||||||
|
|
||||||
let entities = Vec::new(); /*self.ontology
|
|
||||||
.searchable_classes(&*language::ENGLISH_OR_UNTAGGED)
|
|
||||||
.into_iter()
|
|
||||||
.collect::<Vec<_>>();*/
|
|
||||||
|
|
||||||
let type_selector = pick_list(
|
let type_selector = pick_list(
|
||||||
search_state.entity_selection.as_ref(),
|
search_state.entity_class_selection.as_ref(),
|
||||||
entities,
|
Class::ALL,
|
||||||
ToString::to_string,
|
ToString::to_string,
|
||||||
)
|
).on_select(|selection| Message::UpdateQueryClass(selection));
|
||||||
.on_select(|selection| Message::QueryTypeUpdated(selection));
|
|
||||||
|
|
||||||
let mut columns = vec![table::column(text("CURIE"), |_| {
|
let mut columns = vec![table::column(text("CURIE"), |document: &HashMap<gl_search::Field, gl_search::OwnedValue>| {
|
||||||
/*let iri = document
|
let iri = document.get(&Schema::iri_field())
|
||||||
.get_first(Schema::iri_field())
|
|
||||||
.and_then(|value| value.as_str())
|
.and_then(|value| value.as_str())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let abbreviated_iri = self
|
let abbreviated_iri = self.curie_helper
|
||||||
.curie_helper
|
|
||||||
.abbreviate(None, iri)
|
.abbreviate(None, iri)
|
||||||
.unwrap_or_else(|| iri.to_string());
|
.unwrap_or_else(|| iri.to_string());
|
||||||
|
|
||||||
button(text(abbreviated_iri).wrapping(Wrapping::Word))
|
button(text(abbreviated_iri).wrapping(Wrapping::Word))
|
||||||
.on_press(Message::SearchResultClicked(NamedNode::new_unchecked(iri)))
|
.on_press(Message::SearchResultClicked(NamedNode::new_unchecked(iri)))
|
||||||
.style(button::text)*/
|
.style(button::text)
|
||||||
text("")
|
|
||||||
})];
|
})];
|
||||||
|
|
||||||
let fields = if let Some(selection) = search_state.entity_selection.as_ref() {
|
/*for field in {
|
||||||
self.ontology
|
|
||||||
.fields_for_class(&selection.iri, &*language::ENGLISH_OR_UNTAGGED)
|
|
||||||
.expect("Unable to load fields for class")
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
};
|
|
||||||
|
|
||||||
/*for field in fields.into_iter() {
|
|
||||||
let field_name = field.name.clone();
|
let field_name = field.name.clone();
|
||||||
columns.push(
|
columns.push(
|
||||||
table::column(text(field.label), move |document: &SearchDocument| {
|
table::column(text(field.label), move |document: &SearchDocument| {
|
||||||
@@ -847,9 +878,7 @@ impl Publisher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let back_button = button("\u{1f870}").on_press(Message::NavigateBack);
|
let back_button = button("\u{1f870}").on_press(Message::NavigateBack);
|
||||||
|
|
||||||
let forward_button = button("\u{1f872}").on_press(Message::NavigateForward);
|
let forward_button = button("\u{1f872}").on_press(Message::NavigateForward);
|
||||||
|
|
||||||
let add_row_button = button("Add row").on_press(Message::AddRow(None));
|
let add_row_button = button("Add row").on_press(Message::AddRow(None));
|
||||||
|
|
||||||
let address_input = iri_input(&self.curie_helper, "URL", None, &self.url_input)
|
let address_input = iri_input(&self.curie_helper, "URL", None, &self.url_input)
|
||||||
@@ -861,28 +890,29 @@ impl Publisher {
|
|||||||
));
|
));
|
||||||
|
|
||||||
let mut rows: Vec<Element<Message>> = vec![];
|
let mut rows: Vec<Element<Message>> = vec![];
|
||||||
rows = self
|
rows = self.document
|
||||||
.document
|
|
||||||
.dataset()
|
.dataset()
|
||||||
.iter_both()
|
.iter_both()
|
||||||
.filter(|(_, _, state)| self.show_read_only || !state.read_only)
|
.filter(|(_, _, state)| self.show_read_only || !state.read_only)
|
||||||
.map(|(key, quad, state)| self.view_row(key, quad, state))
|
.map(|(key, quad, state)| self.view_row(key, quad, state))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let body: Element<Message> = if self.show_new_document_buttons {
|
let body: Element<Message> = /*if self.show_new_document_buttons {
|
||||||
let subclasses = Vec::new(); // TODO self.ontology.subclasses_of(vocab::rda::ENTITY.into_owned());
|
let subclasses = Vec::new(); // TODO self.ontology.subclasses_of(vocab::rda::ENTITY.into_owned());
|
||||||
let labeled_subclasses = subclasses
|
let labeled_subclasses = subclasses
|
||||||
.iter()
|
.iter()
|
||||||
.map(|iri| self.ontology.info(iri, &*language::ENGLISH_OR_UNTAGGED));
|
.map(|iri| self.ontology.info(iri, &*language::ENGLISH_OR_UNTAGGED));
|
||||||
column![self.view_new_entity_buttons(labeled_subclasses)].into()
|
column![self.view_new_entity_buttons(labeled_subclasses)].into()
|
||||||
} else {
|
} else {*/
|
||||||
column(rows).into()
|
column(rows).into();
|
||||||
};
|
//};
|
||||||
|
|
||||||
let inference_table = if self.show_inferred_properties {
|
let inference_table = if self.show_inferred_triples {
|
||||||
let subject_column = table::column("Subject", |triple: TripleRef| {
|
let subject_column = table::column("Subject", |triple: TripleRef| {
|
||||||
let curie = if let NamedOrBlankNodeRef::NamedNode(subject) = triple.subject {
|
let curie = if let NamedOrBlankNodeRef::NamedNode(subject) = triple.subject {
|
||||||
self.curie_helper.abbreviate(None, subject.as_str())
|
Some(self.curie_helper
|
||||||
|
.abbreviate(None, subject.as_str())
|
||||||
|
.unwrap_or_else(|| subject.as_str().to_string()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@@ -891,18 +921,19 @@ impl Publisher {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let predicate_column = table::column("Predicate", |triple: TripleRef| {
|
let predicate_column = table::column("Predicate", |triple: TripleRef| {
|
||||||
let predicate_info = self.ontology.info(
|
let curie = self.curie_helper
|
||||||
&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())
|
.abbreviate(None, triple.predicate.as_str())
|
||||||
.unwrap_or(triple.predicate.as_str().to_string())
|
.unwrap_or_else(|| triple.predicate.as_str().to_string());
|
||||||
|
|
||||||
|
let label = self.resource_descriptions
|
||||||
|
.get(&triple.predicate.into_owned())
|
||||||
|
.and_then(|description| description.label.clone());
|
||||||
|
|
||||||
|
if let Some(label) = label {
|
||||||
|
text(format!("{curie} ({label})"))
|
||||||
} else {
|
} else {
|
||||||
predicate_info.label
|
text(curie)
|
||||||
};
|
}
|
||||||
text(label)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let object_column = table::column("Object", |triple: TripleRef| {
|
let object_column = table::column("Object", |triple: TripleRef| {
|
||||||
@@ -923,21 +954,15 @@ impl Publisher {
|
|||||||
save_button_base
|
save_button_base
|
||||||
};
|
};
|
||||||
|
|
||||||
let inferred_type_toggle =
|
let inferred_triples_toggle =
|
||||||
toggler(self.show_inferred_types).on_toggle(Message::SetInferredTypes);
|
toggler(self.show_inferred_triples).on_toggle(Message::SetShowInferredTriples);
|
||||||
|
|
||||||
let inferred_property_toggle =
|
let read_only_toggle = toggler(self.show_read_only).on_toggle(Message::SetShowReadOnly);
|
||||||
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![
|
let footer = row![
|
||||||
space::horizontal(),
|
space::horizontal(),
|
||||||
text("Show Inferred Types"),
|
text("Show Inferred Triples"),
|
||||||
inferred_type_toggle,
|
inferred_triples_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
|
||||||
|
|||||||
+7
-1
@@ -7,10 +7,13 @@ pub(crate) struct QueryArgs {
|
|||||||
pub(crate) dataset_path: Option<PathBuf>,
|
pub(crate) dataset_path: Option<PathBuf>,
|
||||||
|
|
||||||
#[arg(short, long, value_name = "QUERY PATH")]
|
#[arg(short, long, value_name = "QUERY PATH")]
|
||||||
pub(crate) query_path: PathBuf,
|
pub(crate) query_path: Option<PathBuf>,
|
||||||
|
|
||||||
#[arg(short, long, value_name = "BASE IRI")]
|
#[arg(short, long, value_name = "BASE IRI")]
|
||||||
pub(crate) base: Option<String>,
|
pub(crate) base: Option<String>,
|
||||||
|
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub(crate) inferences_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
@@ -18,6 +21,9 @@ pub(crate) struct SearchArgs {
|
|||||||
#[arg(short, long, value_name = "DOC TYPE")]
|
#[arg(short, long, value_name = "DOC TYPE")]
|
||||||
pub(crate) discriminant: Option<u64>,
|
pub(crate) discriminant: Option<u64>,
|
||||||
|
|
||||||
|
#[arg(short, long, value_name = "LIMIT")]
|
||||||
|
pub(crate) limit: Option<usize>,
|
||||||
|
|
||||||
#[arg(value_name = "QUERY")]
|
#[arg(value_name = "QUERY")]
|
||||||
pub(crate) query: String,
|
pub(crate) query: String,
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-9
@@ -5,7 +5,7 @@ mod navigator;
|
|||||||
mod rdf;
|
mod rdf;
|
||||||
mod theme;
|
mod theme;
|
||||||
mod widget;
|
mod widget;
|
||||||
mod windows;
|
mod tasks;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::app::Publisher;
|
use crate::app::Publisher;
|
||||||
@@ -42,25 +42,23 @@ fn main() -> color_eyre::Result<()> {
|
|||||||
.init();
|
.init();
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
|
||||||
let mut index = SearchIndex::builder()
|
|
||||||
.with_path("/home/alex/.local/share/org.graphofliberty.desktop/index")
|
|
||||||
.build()
|
|
||||||
.expect("Failed to build search index");
|
|
||||||
|
|
||||||
let args = AppArgs::parse();
|
let args = AppArgs::parse();
|
||||||
match args.command {
|
match args.command {
|
||||||
Some(Command::Query(args)) => {
|
Some(Command::Query(args)) => {
|
||||||
let raw_query = String::from_utf8(std::fs::read(&args.query_path)?)?;
|
let raw_query = if let Some(query) = &args.query_path {
|
||||||
|
Some(String::from_utf8(std::fs::read(query)?)?)
|
||||||
|
} else { None };
|
||||||
|
|
||||||
let graph = if let Some(dataset_path) = &args.dataset_path {
|
let graph = if let Some(dataset_path) = &args.dataset_path {
|
||||||
Some(String::from_utf8(std::fs::read(dataset_path)?)?)
|
Some(String::from_utf8(std::fs::read(dataset_path)?)?)
|
||||||
} else { None };
|
} else { None };
|
||||||
|
|
||||||
let mut request = OntologyQueryRequest::default();
|
let mut request = OntologyQueryRequest::default();
|
||||||
request.sparql_query = Some(raw_query);
|
request.sparql_query = raw_query;
|
||||||
request.turtle = graph;
|
request.turtle = graph;
|
||||||
request.prefixes = HashMap::from_iter(gl_graph::PREFIXES.iter().map(|(name, iri)| (name.clone(), iri.clone())));
|
request.prefixes = HashMap::from_iter(gl_graph::PREFIXES.iter().map(|(name, iri)| (name.clone(), iri.clone())));
|
||||||
request.base = args.base.clone();
|
request.base = args.base.clone();
|
||||||
|
request.inferences_only = args.inferences_only;
|
||||||
|
|
||||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||||
.enable_all()
|
.enable_all()
|
||||||
@@ -74,11 +72,22 @@ fn main() -> color_eyre::Result<()> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
Some(Command::Search(args)) => {
|
Some(Command::Search(args)) => {
|
||||||
for document in index.query(args.discriminant, &args.query, Schema::all_fields(), 5000)? {
|
let mut index = SearchIndex::builder()
|
||||||
|
.with_path("/home/alex/.local/share/org.graphofliberty.desktop/index")
|
||||||
|
.build()
|
||||||
|
.expect("Failed to build search index");
|
||||||
|
|
||||||
|
let limit = args.limit.unwrap_or(5);
|
||||||
|
for document in index.query(args.discriminant, &args.query, Schema::all_fields(), limit)? {
|
||||||
println!("{}", gl_search::to_json(document));
|
println!("{}", gl_search::to_json(document));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Command::Reindex) => {
|
Some(Command::Reindex) => {
|
||||||
|
let mut index = SearchIndex::builder()
|
||||||
|
.with_path("/home/alex/.local/share/org.graphofliberty.desktop/index")
|
||||||
|
.build()
|
||||||
|
.expect("Failed to build search index");
|
||||||
|
|
||||||
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()?;
|
||||||
@@ -147,6 +156,7 @@ fn main() -> color_eyre::Result<()> {
|
|||||||
request.turtle = Some(turtle);
|
request.turtle = Some(turtle);
|
||||||
request.prefixes = HashMap::new();
|
request.prefixes = HashMap::new();
|
||||||
request.base = None;
|
request.base = None;
|
||||||
|
request.inferences_only = false;
|
||||||
|
|
||||||
let response = client.query(request).await?;
|
let response = client.query(request).await?;
|
||||||
let graph_with_inferences = RdfParser::from_format(RdfFormat::Turtle)
|
let graph_with_inferences = RdfParser::from_format(RdfFormat::Turtle)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
pub(crate) mod conversion;
|
pub(crate) mod conversion;
|
||||||
pub(crate) mod ontology;
|
//pub(crate) mod ontology;
|
||||||
pub(crate) mod term_helper;
|
pub(crate) mod term_helper;
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
use iced::Task;
|
||||||
|
use oxigraph::model::{Graph, NamedNode};
|
||||||
|
use gl_graph::language;
|
||||||
|
use gl_graph::ontology::Ontology;
|
||||||
|
use crate::app::Message;
|
||||||
|
|
||||||
|
pub(crate) fn connect_to_ontology_service(endpoint: String) -> Task<Message> {
|
||||||
|
Task::future(Ontology::new(endpoint, (&*language::ENGLISH_OR_UNTAGGED).clone()))
|
||||||
|
.then(|result| {
|
||||||
|
match result {
|
||||||
|
Ok(ontology) => Task::done(Message::ConnectedToOntologyService(ontology)),
|
||||||
|
Err(err) => Task::done(Message::ShowError(err.to_string()))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn lookup_resource_description(mut ontology: Ontology, subject: NamedNode) -> Task<Message> {
|
||||||
|
let subject_clone = subject.clone();
|
||||||
|
Task::perform(async move { ontology.resource_description(subject_clone).await }, |result| {
|
||||||
|
match result {
|
||||||
|
Ok(description) => Message::CacheResourceDescription(subject, description),
|
||||||
|
Err(err) => Message::ShowError(err.to_string()),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn list_datatypes(mut ontology: Ontology) -> Task<Message> {
|
||||||
|
Task::perform(async move { ontology.datatypes().await }, |result| {
|
||||||
|
match result {
|
||||||
|
Ok(datatypes) => Message::CacheDatatypes(datatypes),
|
||||||
|
Err(err) => Message::ShowError(err.to_string()),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn list_read_only_entities(mut ontology: Ontology) -> Task<Message> {
|
||||||
|
Task::perform(async move { ontology.list_read_only().await }, |result| {
|
||||||
|
match result {
|
||||||
|
Ok(entities) => Message::CacheReadOnlyEntities(entities),
|
||||||
|
Err(err) => Message::ShowError(err.to_string()),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn run_inference(mut ontology: Ontology, graph: Graph) -> Task<Message> {
|
||||||
|
Task::perform(async move { ontology.run_inference(&graph).await }, |result| {
|
||||||
|
match result {
|
||||||
|
Ok(inferences) => Message::SetInferredTriples(inferences),
|
||||||
|
Err(err) => Message::ShowError(err.to_string()),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
Reference in New Issue
Block a user