This commit is contained in:
2026-08-28 15:57:59 -04:00
parent 277d1d2a2d
commit 1a122d6ecc
12 changed files with 90 additions and 89 deletions
+1 -3
View File
@@ -1,10 +1,8 @@
use std::collections::HashMap;
use std::fmt::Display;
use oxigraph::model::{Graph, Literal, NamedNode, NamedNodeRef, NamedOrBlankNode, Term, TermRef, Triple};
use oxigraph::model::{Graph, Literal, NamedNode, NamedNodeRef, NamedOrBlankNode, Term, Triple};
use oxilangtag::LanguageTag;
use url::Url;
use gl_search::Schema;
use crate::language::LanguageCondition;
use crate::vocab;
#[derive(Clone, Debug, Eq, PartialEq)]
+1 -2
View File
@@ -1,6 +1,5 @@
use std::collections::BTreeMap;
use std::sync::LazyLock;
use oxigraph::model::NamedNode;
use url::Url;
const ONTOLOGY_PREFIX: &str = "https://graphofliberty.org/2026/04/ont/";
@@ -87,7 +86,7 @@ impl CurieHelper {
absolute_iri.split_once(':')
.and_then(|(prefix, name)| {
if prefix == "" && let Some(base) = base {
if prefix.is_empty() && let Some(base) = base {
Some(format!("{base}{name}"))
} else {
self.prefixes
+2 -5
View File
@@ -2,7 +2,6 @@ use rayon::iter::ParallelIterator;
use std::collections::{HashMap, HashSet};
use oxigraph::model::{Graph, NamedNode, TermRef, NamedOrBlankNodeRef, NamedNodeRef};
use rayon::iter::IntoParallelRefIterator;
use url::Url;
use gl_search::{Field, OwnedValue, Schema};
use crate::class::Class;
use crate::{helpers, vocab, CurieHelper};
@@ -90,7 +89,7 @@ impl<'a> Indexer<'a> {
}
}
let results = entities_and_types.par_iter()
entities_and_types.par_iter()
.filter_map(|(subject, class)| {
Class::try_from_named_node(*class).and_then(|class| {
match class {
@@ -114,9 +113,7 @@ impl<'a> Indexer<'a> {
document
})
})
}).collect();
results
}).collect()
}
pub fn ontology(&self, entities: HashMap<NamedNode, ResourceDescription>) -> Vec<HashMap<Field, OwnedValue>> {
+1 -3
View File
@@ -1,10 +1,8 @@
use std::env::var;
use oxigraph::model::{Literal, TermRef};
use oxilangtag::LanguageTag;
use std::sync::LazyLock;
use spargebra::algebra::{Expression, Function, GraphPattern};
use spargebra::term::{NamedNode, NamedNodePattern, TermPattern, TriplePattern, Variable};
use tracing::debug;
use spargebra::term::Variable;
pub static ENGLISH_TAG: LazyLock<LanguageTag<String>> = LazyLock::new(|| {
LanguageTag::parse("en".to_string()).unwrap()
+26 -17
View File
@@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use std::str::FromStr;
use oxigraph::io::{RdfFormat, RdfParser, RdfSerializer};
use oxigraph::model::{Graph, NamedNode, NamedNodeRef, Triple};
use oxigraph::model::{Graph, NamedNode, Triple};
use oxigraph::sparql::results::{QueryResultsFormat, QueryResultsParser, SliceQueryResultsParserOutput};
use spargebra::algebra::GraphPattern;
use spargebra::term::{GroundTerm, NamedNodePattern, TermPattern, TriplePattern, Variable};
@@ -86,11 +86,11 @@ impl ConnectedOntology {
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 request = OntologyQueryRequest {
turtle: Some(turtle),
inferences_only: true,
..Default::default()
};
let response = self.client.query(request).await?;
Ok(RdfParser::from_format(RdfFormat::Turtle)
@@ -101,8 +101,7 @@ impl ConnectedOntology {
}
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#>
let sparql_query = 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/>
@@ -111,7 +110,12 @@ SELECT ?subject ?class WHERE {{
VALUES ?class {{ rdf:Property rdfs:Class }}
?subject a ?class ;
gl:readOnly "true"^^xsd:boolean .
}}"#));
}}"#.to_owned();
let request = OntologyQueryRequest {
sparql_query: Some(sparql_query),
..Default::default()
};
let response = self.client.query(request).await?;
let parser_output = QueryResultsParser::from_format(QueryResultsFormat::Json)
@@ -123,12 +127,12 @@ SELECT ?subject ?class WHERE {{
let subject = solution
.get("subject")
.and_then(term_to_named_node)
.map(|node| node.clone());
.cloned();
let class = solution
.get("class")
.and_then(term_to_named_node)
.map(|node| node.clone());
.cloned();
if let Some(subject) = subject &&
let Some(class) = class {
@@ -186,8 +190,7 @@ SELECT ?subject ?class WHERE {{
let comment_filter = self.language.filter("comment");
let definition_filter = self.language.filter("definition");
let mut request = OntologyQueryRequest::default();
request.sparql_query = Some(format!(r#"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
let sparql_query = 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#>
@@ -204,7 +207,12 @@ SELECT ?subject ?label ?description ?class WHERE {{
{definition_filter}
}}
BIND(COALESCE(?definition, ?comment) AS ?description)
}}"#));
}}"#);
let request = OntologyQueryRequest {
sparql_query: Some(sparql_query),
..Default::default()
};
let response = self.client.query(request).await?;
let parser_output = QueryResultsParser::from_format(QueryResultsFormat::Json)
@@ -215,12 +223,13 @@ SELECT ?subject ?label ?description ?class WHERE {{
for solution in solutions.filter_map(Result::ok) {
let class = solution
.get("class")
.and_then(term_to_named_node);
.and_then(term_to_named_node)
.cloned();
let subject = solution
.get("subject")
.and_then(term_to_named_node)
.map(|node| node.clone());
.cloned();
if let Some(subject) = subject {
let label = solution
@@ -236,7 +245,7 @@ SELECT ?subject ?label ?description ?class WHERE {{
results.insert(subject, ResourceDescription {
label,
description,
class: class.cloned(),
class,
});
}
}
+3 -3
View File
@@ -44,9 +44,9 @@ fn run_update(query_name: &str, query: &str, store: &Store, graph_name: GraphNam
pub(crate) fn infer(inferences: usize, store: &Store, graph_name: GraphNameRef<'_>) -> crate::error::Result<usize> {
let old_count = store.quads_for_pattern(None, None, None, Some(graph_name)).count();
run_update("rdfs:subPropertyOf", SUB_PROPERTY_OF_UPDATE, &store, graph_name)?;
run_update("rdfs:subClassOf", SUB_CLASS_OF_UPDATE, &store, graph_name)?;
run_update("rdfs:domain", DOMAIN_UPDATE, &store, graph_name)?;
run_update("rdfs:subPropertyOf", SUB_PROPERTY_OF_UPDATE, store, graph_name)?;
run_update("rdfs:subClassOf", SUB_CLASS_OF_UPDATE, store, graph_name)?;
run_update("rdfs:domain", DOMAIN_UPDATE, store, graph_name)?;
let new_count = store.quads_for_pattern(None, None, None, Some(graph_name)).count();
let new_inferences_total = inferences + (new_count - old_count);
+16 -7
View File
@@ -31,16 +31,15 @@ impl Ontology for OntologyService {
let span = debug_span!("Load Ontology", old_size = field::Empty, input_size = field::Empty, inferences = field::Empty, new_size = field::Empty).entered();
let request = request.get_ref();
let mut response = OntologyLoadResponse::default();
response.old_size = self.ontology.len()
let old_size = self.ontology.len()
.map_err(|err| Status::internal(err.to_string()))?
.to_u64()
.unwrap_or(u64::MAX);
let path = std::path::Path::new(&request.path);
let store = Store::open_read_only(path).unwrap();
response.input_size = store.len()
let input_size = store.len()
.map_err(|err| Status::internal(err.to_string()))?
.to_u64()
.unwrap_or(u64::MAX);
@@ -53,18 +52,25 @@ impl Ontology for OntologyService {
});
self.ontology.extend(quads).unwrap();
response.inferences = if request.infer {
let inferences = if request.infer {
crate::logic::infer(0, &self.ontology, ONTOLOGY_GRAPH)
.map_err(|err| Status::internal(err.to_string()))?
.to_u64()
.unwrap_or(u64::MAX)
} else { 0 };
response.new_size = self.ontology.len()
let new_size = self.ontology.len()
.map_err(|err| Status::internal(err.to_string()))?
.to_u64()
.unwrap_or(u64::MAX);
let response = OntologyLoadResponse {
old_size,
input_size,
inferences,
new_size,
};
span.record("old_size", response.old_size);
span.record("input_size", response.input_size);
span.record("inferences", response.inferences);
@@ -76,8 +82,7 @@ impl Ontology for OntologyService {
async fn clear(&self, _request: Request<()>) -> Result<Response<OntologyClearResponse>, Status> {
let span = debug_span!("Clear Ontology", size = field::Empty).entered();
let mut response = OntologyClearResponse::default();
response.size = self.ontology.len()
let size = self.ontology.len()
.map_err(|err| Status::internal(err.to_string()))?
.to_u64()
.unwrap_or(u64::MAX);
@@ -85,6 +90,10 @@ impl Ontology for OntologyService {
self.ontology.clear()
.map_err(|err| Status::internal(err.to_string()))?;
let response = OntologyClearResponse {
size,
};
span.record("size", response.size);
Ok(Response::new(response))
+8 -13
View File
@@ -245,11 +245,7 @@ impl Publisher {
}
}
Message::URLInputChanged(value) => {
if Url::parse(&value).is_ok() {
self.url_input_valid = true;
} else {
self.url_input_valid = false;
}
self.url_input_valid = Url::parse(&value).is_ok();
self.url_input = value;
}
Message::URLInputSubmitted => {
@@ -295,13 +291,13 @@ impl Publisher {
task = Task::done(Message::ShowNewDocumentButtons);
}
Message::LoadDocument(document) => {
let nodes_to_look_up = document.dataset().quads.iter().map(|(_, quad)| {
let nodes_to_look_up = document.dataset().quads.iter().flat_map(|(_, quad)| {
let mut nodes = vec![ quad.predicate.clone() ];
if let Some(object) = term_to_named_node(&quad.object) {
nodes.push(object.clone());
}
nodes
}).flatten().collect();
}).collect();
let add_row_tasks = document.dataset().quads.iter().map(|(key, quad)| {
let datatype_state = combo_box::State::new(self.abbreviated_datatypes.clone());
@@ -578,12 +574,11 @@ impl Publisher {
}
}
Message::NavigateToObject(key) => {
if let Some(quad) = self.document.dataset_mut().quads.get_mut(key) {
if let Term::NamedNode(node) = &quad.object {
if let Some(quad) = self.document.dataset_mut().quads.get_mut(key)
&& let Term::NamedNode(node) = &quad.object {
let url = Url::parse(node.as_str()).expect("Invalid URL");
task = Task::done(Message::URLInputChanged(url.to_string()))
.chain(Task::done(Message::URLInputSubmitted));
}
}
}
Message::NavigateBack => {
@@ -836,7 +831,7 @@ impl Publisher {
search_state.entity_class_selection.as_ref(),
Class::ALL,
ToString::to_string,
).on_select(|selection| Message::UpdateQueryClass(selection));
).on_select(Message::UpdateQueryClass);
let mut columns = vec![table::column(text("CURIE"), |document: &HashMap<gl_search::Field, gl_search::OwnedValue>| {
let iri = document.get(&Schema::iri_field())
@@ -888,8 +883,8 @@ impl Publisher {
}
pub(crate) fn view(&self, window: window::Id) -> Element<'_, Message> {
if let Some(search_state) = &self.search_state
&& search_state.window_id == window {
if let Some(search_state) = &self.search_state &&
search_state.window_id == window {
return self.view_search_window(search_state);
}
+10 -10
View File
@@ -17,8 +17,7 @@ use ldp::middleware::BasicAuthMiddleware;
use ldp::reqwest::Client;
use ldp::reqwest_middleware::ClientBuilder;
use ldp::traverse::Traverse;
use oxigraph::io::{RdfFormat, RdfParser, RdfSerializer};
use oxigraph::model::{Dataset, Graph, Triple, TripleRef};
use oxigraph::model::{Graph, TripleRef};
use tracing::{debug_span, error, field, Instrument};
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::layer::SubscriberExt;
@@ -54,12 +53,13 @@ fn main() -> color_eyre::Result<()> {
Some(String::from_utf8(std::fs::read(dataset_path)?)?)
} else { None };
let mut request = OntologyQueryRequest::default();
request.sparql_query = raw_query;
request.turtle = graph;
request.prefixes = HashMap::from_iter(gl_graph::PREFIXES.iter().map(|(name, iri)| (name.clone(), iri.clone())));
request.base = args.base.clone();
request.inferences_only = args.inferences_only;
let request = OntologyQueryRequest {
sparql_query: raw_query,
turtle: graph,
prefixes: HashMap::from_iter(gl_graph::PREFIXES.iter().map( | (name, iri) | (name.clone(), iri.clone()))),
base: args.base.clone(),
inferences_only: args.inferences_only,
};
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
@@ -77,7 +77,7 @@ fn main() -> color_eyre::Result<()> {
});
}
Some(Command::Search(args)) => {
let mut index = SearchIndex::builder()
let index = SearchIndex::builder()
.with_path("/home/alex/.local/share/org.graphofliberty.desktop/index")
.build()
.expect("Failed to build search index");
@@ -142,7 +142,7 @@ fn main() -> color_eyre::Result<()> {
Ok(rdf_source) => {
let triples = rdf_source.dataset()
.iter()
.map(|quad| TripleRef::from(quad));
.map(TripleRef::from);
graph.extend(triples);
rdf_source_count += 1;
},
+1 -2
View File
@@ -2,8 +2,7 @@ use iced::Task;
use ldp::reqwest_middleware::ClientWithMiddleware;
use ldp::ResourceRequestBuilder;
use oxigraph::io::RdfFormat;
use oxigraph::model::{Dataset, Graph, NamedNode, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, TermRef};
use tracing::debug;
use oxigraph::model::{Dataset, Graph, NamedNode, NamedNodeRef, NamedOrBlankNodeRef, TermRef};
use url::Url;
use gl_graph::class::Class;
use gl_graph::ontology::{ConnectedOntology, OntologyBuilder, ResourceSelector};
+18 -21
View File
@@ -71,10 +71,9 @@ where
#[must_use]
pub fn on_input(mut self, on_input: impl Fn(String) -> Message + 'a) -> Self {
let base_clone = self.base.clone();
let wrapped = move |value: String| {
let expanded_value = self.curie_helper.expand(base_clone, &value);
on_input(expanded_value.unwrap_or_else(|| value))
let expanded_value = self.curie_helper.expand(self.base, &value);
on_input(expanded_value.unwrap_or(value))
};
self.text_input = self.text_input.on_input(wrapped);
@@ -214,21 +213,20 @@ where
state.control = modifiers.control();
state.shift = modifiers.shift();
}
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
if cursor.is_over(layout.bounds()) {
if state.control
&& let Some(on_control_click) = &self.on_control_click
{
shell.publish(on_control_click.clone());
shell.capture_event();
}
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
if cursor.is_over(layout.bounds()) => {
if state.control
&& let Some(on_control_click) = &self.on_control_click
{
shell.publish(on_control_click.clone());
shell.capture_event();
}
if state.shift
&& let Some(on_shift_click) = &self.on_shift_click
{
shell.publish(on_shift_click.clone());
shell.capture_event();
}
if state.shift
&& let Some(on_shift_click) = &self.on_shift_click
{
shell.publish(on_shift_click.clone());
shell.capture_event();
}
}
_ => {}
@@ -246,10 +244,9 @@ where
);
let clipboard = shell.clipboard_mut();
if let Some(Content::Text(content)) = &clipboard.write {
if let Some(expanded) = self.curie_helper.expand(self.base, &content) {
clipboard.write = Some(Content::Text(expanded));
}
if let Some(Content::Text(content)) = &clipboard.write &&
let Some(expanded) = self.curie_helper.expand(self.base, content) {
clipboard.write = Some(Content::Text(expanded));
}
}
+2 -2
View File
@@ -1,7 +1,7 @@
use subtitler;
/*use subtitler;
use subtitler::SubtitleFormat;
/*
pub fn bytes_to_documents(data: &[u8]) -> crate::Result<Vec<SearchDocument>> {
let subtitle_start = Schema::field("subtitle_start", None);
let subtitle_end = Schema::field("subtitle_end", None);