diff --git a/src/annotation.rs b/src/annotation.rs index f7d14c5..25206d3 100644 --- a/src/annotation.rs +++ b/src/annotation.rs @@ -92,6 +92,8 @@ impl PyAnnotation { annotation: Some(annotation.handle()), resource: None, dataset: None, + key: None, + data: None, offset: if annotation .as_ref() .target() diff --git a/src/annotationdata.rs b/src/annotationdata.rs index 6b42836..f8d218e 100644 --- a/src/annotationdata.rs +++ b/src/annotationdata.rs @@ -12,6 +12,7 @@ use crate::annotationdataset::PyAnnotationDataSet; use crate::annotationstore::MapStore; use crate::error::PyStamError; use crate::query::*; +use crate::selector::{PySelector, PySelectorKind}; use stam::*; #[pyclass(dict, module = "stam", name = "DataKey")] @@ -145,6 +146,24 @@ impl PyDataKey { fn annotations_count(&self) -> usize { self.map(|key| Ok(key.annotations_count())).unwrap() } + + /// Returns a Selector (DataKeySelector) pointing to this DataKey + fn select(&self) -> PyResult { + self.map(|key| { + Ok(PySelector { + kind: PySelectorKind { + kind: SelectorKind::DataKeySelector, + }, + dataset: None, + annotation: None, + resource: None, + key: Some((key.set().handle(), key.handle())), + data: None, + offset: None, + subselectors: Vec::new(), + }) + }) + } } impl MapStore for PyDataKey { @@ -479,6 +498,24 @@ impl PyAnnotationData { fn annotations_len(&self) -> usize { self.map(|data| Ok(data.annotations_len())).unwrap() } + + /// Returns a Selector (AnnotationDataSelector) pointing to this AnnotationData + fn select(&self) -> PyResult { + self.map(|data| { + Ok(PySelector { + kind: PySelectorKind { + kind: SelectorKind::AnnotationDataSelector, + }, + dataset: None, + annotation: None, + resource: None, + data: Some((data.set().handle(), data.handle())), + key: None, + offset: None, + subselectors: Vec::new(), + }) + }) + } } impl MapStore for PyAnnotationData { diff --git a/src/annotationdataset.rs b/src/annotationdataset.rs index 5fbab17..86d4ce5 100644 --- a/src/annotationdataset.rs +++ b/src/annotationdataset.rs @@ -220,6 +220,8 @@ impl PyAnnotationDataSet { dataset: Some(dataset.handle()), annotation: None, resource: None, + key: None, + data: None, offset: None, subselectors: Vec::new(), }) diff --git a/src/resources.rs b/src/resources.rs index dae6179..9315188 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -302,6 +302,8 @@ impl PyTextResource { resource: Some(resource.handle()), annotation: None, dataset: None, + key: None, + data: None, offset: None, subselectors: Vec::new(), }) diff --git a/src/selector.rs b/src/selector.rs index 19e8508..4bbd430 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -3,6 +3,7 @@ use pyo3::prelude::*; use pyo3::pyclass::CompareOp; use crate::annotation::PyAnnotation; +use crate::annotationdata::{PyAnnotationData, PyDataKey}; use crate::annotationdataset::PyAnnotationDataSet; use crate::annotationstore::{MapStore, PyAnnotationStore}; use crate::resources::{PyOffset, PyTextResource}; @@ -63,6 +64,8 @@ pub(crate) struct PySelector { pub(crate) resource: Option, pub(crate) annotation: Option, pub(crate) dataset: Option, + pub(crate) key: Option<(AnnotationDataSetHandle, DataKeyHandle)>, + pub(crate) data: Option<(AnnotationDataSetHandle, AnnotationDataHandle)>, pub(crate) offset: Option, pub(crate) subselectors: Vec, } @@ -93,6 +96,26 @@ impl PySelector { .expect("pyselector of type datasetselector must have dataset, was checked on instantiation") .into(), ), + SelectorKind::DataKeySelector => SelectorBuilder::DataKeySelector( + self.key + .expect("pyselector of type datakeyselector must have key, was checked on instantiation") + .0 + .into(), + self.key + .expect("pyselector of type datakeyselector must have key, was checked on instantiation") + .1 + .into(), + ), + SelectorKind::AnnotationDataSelector => SelectorBuilder::AnnotationDataSelector( + self.data + .expect("pyselector of type annotationdataselector must have key, was checked on instantiation") + .0 + .into(), + self.data + .expect("pyselector of type annotationdataselector must have key, was checked on instantiation") + .1 + .into(), + ), SelectorKind::MultiSelector => { SelectorBuilder::MultiSelector(self.subselectors.iter().map(|subselector| subselector.build()).collect()) } @@ -110,12 +133,14 @@ impl PySelector { #[pymethods] impl PySelector { #[new] - #[pyo3(signature = (kind, resource=None, annotation=None, dataset=None, offset=None, subselectors=Vec::new()))] + #[pyo3(signature = (kind, resource=None, annotation=None, dataset=None, key=None, data=None, offset=None, subselectors=Vec::new()))] fn new( kind: &PySelectorKind, resource: Option>, annotation: Option>, dataset: Option>, + key: Option>, + data: Option>, offset: Option>, subselectors: Vec>, ) -> PyResult { @@ -127,6 +152,8 @@ impl PySelector { resource: Some(resource.handle), annotation: None, dataset: None, + key: None, + data: None, offset: None, subselectors: Vec::new(), }) @@ -142,6 +169,8 @@ impl PySelector { annotation: Some(annotation.handle), resource: None, dataset: None, + key: None, + data: None, offset: Some(offset.clone()), subselectors: Vec::new(), }) @@ -151,6 +180,8 @@ impl PySelector { annotation: Some(annotation.handle), resource: None, dataset: None, + key: None, + data: None, offset: None, subselectors: Vec::new(), }) @@ -167,6 +198,8 @@ impl PySelector { resource: Some(resource.handle), annotation: None, dataset: None, + key: None, + data: None, offset: Some(offset.clone()), subselectors: Vec::new(), }) @@ -184,6 +217,8 @@ impl PySelector { resource: None, annotation: None, dataset: Some(dataset.handle), + key: None, + data: None, offset: None, subselectors: Vec::new(), }) @@ -191,6 +226,38 @@ impl PySelector { Err(PyValueError::new_err("'dataset' keyword argument must be specified for DataSetSelector and point to an AnnotationDataSet instance")) } } + SelectorKind::DataKeySelector => { + if let Some(key) = key { + Ok(PySelector { + kind: kind.clone(), + resource: None, + annotation: None, + dataset: None, + key: Some((key.set, key.handle)), + data: None, + offset: None, + subselectors: Vec::new(), + }) + } else { + Err(PyValueError::new_err("'key' keyword argument must be specified for DataKeySelector and point to a DataKey instance")) + } + } + SelectorKind::AnnotationDataSelector => { + if let Some(data) = data { + Ok(PySelector { + kind: kind.clone(), + resource: None, + annotation: None, + dataset: None, + data: Some((data.set, data.handle)), + key: None, + offset: None, + subselectors: Vec::new(), + }) + } else { + Err(PyValueError::new_err("'key' keyword argument must be specified for DataKeySelector and point to a DataKey instance")) + } + } SelectorKind::MultiSelector | SelectorKind::CompositeSelector | SelectorKind::DirectionalSelector => { @@ -202,6 +269,8 @@ impl PySelector { resource: None, annotation: None, dataset: None, + key: None, + data: None, offset: None, subselectors: subselectors.into_iter().map(|sel| sel.clone()).collect(), }) @@ -221,6 +290,8 @@ impl PySelector { Some(resource), None, None, + None, + None, Some(offset), Vec::new(), ) @@ -237,6 +308,8 @@ impl PySelector { None, Some(annotation), None, + None, + None, offset, Vec::new(), ) @@ -251,6 +324,8 @@ impl PySelector { None, None, None, + None, + None, Vec::new(), ) } @@ -264,6 +339,8 @@ impl PySelector { None, Some(annotationset), None, + None, + None, Vec::new(), ) } @@ -278,6 +355,8 @@ impl PySelector { None, None, None, + None, + None, subselectors, ) } @@ -292,6 +371,8 @@ impl PySelector { None, None, None, + None, + None, subselectors, ) } @@ -306,6 +387,8 @@ impl PySelector { None, None, None, + None, + None, subselectors, ) } @@ -370,6 +453,28 @@ impl PySelector { }) } + /// Returns the key this selector points at, if any. + /// Works only for DataKeySelector, returns None otherwise. + /// Requires to explicitly pass the store so the resource can be found. + fn key(&self, store: PyRef) -> Option { + self.key.map(|(set_handle, key_handle)| PyDataKey { + set: set_handle, + handle: key_handle, + store: store.get_store().clone(), + }) + } + + /// Returns the annotationdata this selector points at, if any. + /// Works only for AnnotationDataSelector, returns None otherwise. + /// Requires to explicitly pass the store so the resource can be found. + fn annotationdata(&self, store: PyRef) -> Option { + self.data.map(|(set_handle, data_handle)| PyAnnotationData { + set: set_handle, + handle: data_handle, + store: store.get_store().clone(), + }) + } + /// Returns the annotation this selector points at, if any. /// Works only for AnnotationSelector, returns None otherwise. /// Requires to explicitly pass the store so the resource can be found. @@ -420,6 +525,14 @@ impl PySelector { Selector::AnnotationSelector(a_id, ..) => Some(*a_id), _ => None, }, + key: match selector { + Selector::DataKeySelector(set_id, key_id) => Some((*set_id, *key_id)), + _ => None, + }, + data: match selector { + Selector::AnnotationDataSelector(set_id, data_id) => Some((*set_id, *data_id)), + _ => None, + }, offset: selector.offset(store).map(|offset| PyOffset { offset }), subselectors: if selector.is_complex() { if let Some(subselectors) = selector.subselectors() { diff --git a/stam.pyi b/stam.pyi index b014043..2de9275 100644 --- a/stam.pyi +++ b/stam.pyi @@ -868,6 +868,9 @@ class DataKey: The maximum number of results to return (default: unlimited) """ + def select(self) -> Selector: + """Returns a selector pointing to this key (DataKeySelector)""" + class DataValue: """Encapsulates a value and its type. Held by :class:`AnnotationData`. This type is not a reference but holds the actual value.""" @@ -963,6 +966,9 @@ class AnnotationData: The maximum number of results to return (default: unlimited) """ + def select(self) -> Selector: + """Returns a selector pointing to this data (AnnotationDataSelector)""" + class Data: """ A `Data` object holds an arbitrary collection of annotation data.