Skip to content

Commit

Permalink
Map time information from SaltXML
Browse files Browse the repository at this point in the history
  • Loading branch information
thomaskrause committed Aug 6, 2024
1 parent f5ca1f7 commit bedf582
Show file tree
Hide file tree
Showing 4 changed files with 374 additions and 215 deletions.
2 changes: 1 addition & 1 deletion src/exporter/graphml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ fn media_vis(graph: &AnnotationGraph) -> Result<Vec<Visualizer>, Box<dyn std::er
mappings: None,
});
}
"mp4" | "avi" | "mov" => {
"mp4" | "avi" | "mov" | "webm" => {
vis.push(Visualizer {
element: "node".to_string(),
layer: None,
Expand Down
5 changes: 5 additions & 0 deletions src/importer/saltxml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ enum SaltObject {
Text(String),
Boolean(bool),
Integer(i64),
Float(f64),
Url(String),
Null,
}
Expand All @@ -154,6 +155,9 @@ impl From<&str> for SaltObject {
} else if let Some(value) = value.strip_prefix("N::") {
let value = value.parse::<i64>().unwrap_or_default();
SaltObject::Integer(value)
} else if let Some(value) = value.strip_prefix("F::") {
let value = value.parse::<f64>().unwrap_or_default();
SaltObject::Float(value)
} else {
SaltObject::Null
}
Expand All @@ -167,6 +171,7 @@ impl std::fmt::Display for SaltObject {
SaltObject::Url(val) => write!(f, "{val}"),
SaltObject::Boolean(val) => write!(f, "{val}"),
SaltObject::Integer(val) => write!(f, "{val}"),
SaltObject::Float(val) => write!(f, "{val}"),
SaltObject::Null => write!(f, ""),
}
}
Expand Down
69 changes: 65 additions & 4 deletions src/importer/saltxml/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,26 @@ impl<'a, 'input> DocumentMapper<'a, 'input> {
if let Some(timeline) = timeline {
mapper.map_timeline(&timeline, updates)?;
}
if !mapper.media_files.is_empty() {
mapper.map_media_relations(updates)?;
}

mapper.map_non_token_nodes(updates)?;

Ok(())
}

fn get_tli_node_name(&self, tli: i64) -> String {
format!("{}#tli{tli}", self.document_node_name)
}

fn map_timeline(&mut self, timeline: &Node, updates: &mut GraphUpdate) -> Result<()> {
let number_of_tlis = get_feature_by_qname(timeline, "saltCommon", "SDATA")
.context("Missing SDATA attribute for timeline.")?;
if let SaltObject::Integer(number_of_tlis) = number_of_tlis {
let mut previous_tli = None;
for i in 0..number_of_tlis {
let tli_node_name = format!("{}#tli{i}", self.document_node_name);
let tli_node_name = self.get_tli_node_name(i);
updates.add_event(UpdateEvent::AddNode {
node_name: tli_node_name.clone(),
node_type: "node".to_string(),
Expand Down Expand Up @@ -164,7 +171,7 @@ impl<'a, 'input> DocumentMapper<'a, 'input> {
for tli in start..end {
updates.add_event(UpdateEvent::AddEdge {
source_node: token_id.clone(),
target_node: format!("{}#tli{tli}", self.document_node_name),
target_node: self.get_tli_node_name(tli),
layer: ANNIS_NS.to_string(),
component_type: AnnotationComponentType::Coverage.to_string(),
component_name: "".to_string(),
Expand Down Expand Up @@ -211,7 +218,6 @@ impl<'a, 'input> DocumentMapper<'a, 'input> {
}

fn map_media_datasources(&mut self, updates: &mut GraphUpdate) -> Result<()> {
// TODO: Map time codes from the SAudioRelation as annis::time
for media_node in self
.nodes
.iter()
Expand Down Expand Up @@ -268,6 +274,61 @@ impl<'a, 'input> DocumentMapper<'a, 'input> {
Ok(())
}

fn map_media_relations(&mut self, updates: &mut GraphUpdate) -> Result<()> {
for media_rel in self
.edges
.iter()
.filter(|n| SaltType::from_node(n) == SaltType::MediaRelation)
{
let source_att = media_rel.attribute("source").unwrap_or_default();
let token_node = resolve_element(source_att, "nodes", &self.nodes)
.context("Token referenced in SAudioRelation cannot be resolved")?;
let token_id = get_element_id(&token_node).context("Token has no ID")?;

let start = get_feature_by_qname(media_rel, "salt", "SSTART")
.context("Missing SSTART attribute for SAudioRlation")?;
let end = get_feature_by_qname(media_rel, "salt", "SEND")
.context("Missing SEND attribute for SAudioRelation")?;

if let (SaltObject::Float(start), SaltObject::Float(end)) = (start, end) {
if let Some(covered_tli) = self.token_to_tli.get(&token_id) {
if let (Some(first_tli), Some(last_tli)) =
(covered_tli.first(), covered_tli.last())
{
if first_tli == last_tli {
// Attach start and end time to the same token
updates.add_event(UpdateEvent::AddNodeLabel {
node_name: self.get_tli_node_name(*first_tli),
anno_ns: "annis".to_string(),
anno_name: "time".to_string(),
anno_value: format!("{start}-{end}"),
})?;
} else {
// Attach start time to first token and end time to
// last token
updates.add_event(UpdateEvent::AddNodeLabel {
node_name: self.get_tli_node_name(*first_tli),
anno_ns: "annis".to_string(),
anno_name: "time".to_string(),
anno_value: format!("{start}-"),
})?;
updates.add_event(UpdateEvent::AddNodeLabel {
node_name: self.get_tli_node_name(*last_tli),
anno_ns: "annis".to_string(),
anno_name: "time".to_string(),
anno_value: format!("-{end}"),
})?;
}
}
}
} else {
bail!("SSTART/SEND not a float")
}
}

Ok(())
}

fn map_node(
&self,
n: &Node,
Expand Down Expand Up @@ -589,7 +650,7 @@ impl<'a, 'input> DocumentMapper<'a, 'input> {
if let Some(tli_token) = self.token_to_tli.get(&target_node_id) {
// Add a coverage edge to the indirectly covered timeline item token
for tli in tli_token {
let tli_id = format!("{}#tli{tli}", &self.document_node_name);
let tli_id = self.get_tli_node_name(*tli);
self.map_edge(
spanning_rel,
Some(tli_id),
Expand Down
Loading

0 comments on commit bedf582

Please sign in to comment.