Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions packages/elf-service/src/knowledge/watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod candidates;
mod inputs;
mod outcomes;
mod outputs;
mod states;
mod summary;

pub(super) use self::{
Expand All @@ -14,10 +15,8 @@ pub(super) use self::{
rebuild_request_from_page,
},
outcomes::{blocked_watch_rebuild, successful_watch_rebuild},
outputs::{
blocked_outputs, blocked_section_states, candidate_reasons_by_section, rebuild_outputs,
successful_rebuild_state, successful_section_states,
},
outputs::{blocked_outputs, candidate_reasons_by_section, rebuild_outputs},
states::{blocked_section_states, successful_rebuild_state, successful_section_states},
summary::{page_operator_summary, watch_operator_summary, watch_rebuild_summary},
};

Expand Down
148 changes: 2 additions & 146 deletions packages/elf-service/src/knowledge/watch/outputs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::knowledge::watch::{
BTreeMap, BTreeSet, KnowledgePageChangedSource, KnowledgePageRebuildOutput,
KnowledgePageSection, KnowledgePageSectionRebuildState, KnowledgePageSectionResponse,
KnowledgePageSourceRef, KnowledgeSourceKind, LintDraft, Uuid, Value, serde_json,
KnowledgePageSection, KnowledgePageSourceRef, KnowledgeSourceKind, LintDraft, Uuid, Value,
serde_json,
};

pub(in crate::knowledge) fn rebuild_outputs(
Expand Down Expand Up @@ -159,107 +159,6 @@ pub(in crate::knowledge) fn conflict_outputs(
.collect()
}

pub(in crate::knowledge) fn successful_section_states(
before_sections: &[KnowledgePageSection],
rebuilt_sections: &[KnowledgePageSectionResponse],
outputs: &[KnowledgePageRebuildOutput],
) -> Vec<KnowledgePageSectionRebuildState> {
let output_map = outputs_by_section(outputs);
let before_by_key = before_sections
.iter()
.map(|section| (section.section_key.as_str(), section))
.collect::<BTreeMap<_, _>>();

rebuilt_sections
.iter()
.map(|section| {
let output_types =
output_map.get(section.section_key.as_str()).cloned().unwrap_or_default();
let lint_finding_types = lint_finding_types_for_outputs(&output_types);
let state = section_state(
before_by_key.get(section.section_key.as_str()).copied(),
section,
&output_types,
);

KnowledgePageSectionRebuildState {
section_key: section.section_key.clone(),
heading: section.heading.clone(),
state,
output_types,
lint_finding_types,
}
})
.collect()
}

pub(in crate::knowledge) fn blocked_section_states(
sections: &[KnowledgePageSection],
outputs: &[KnowledgePageRebuildOutput],
) -> Vec<KnowledgePageSectionRebuildState> {
let output_map = outputs_by_section(outputs);

sections
.iter()
.map(|section| {
let output_types =
output_map.get(section.section_key.as_str()).cloned().unwrap_or_default();
let lint_finding_types = lint_finding_types_for_outputs(&output_types);
let state = if output_types.iter().any(|kind| kind == "missing_citation") {
"blocked"
} else if output_types.iter().any(|kind| kind == "stale_section") {
"stale"
} else {
"blocked"
};

KnowledgePageSectionRebuildState {
section_key: section.section_key.clone(),
heading: section.heading.clone(),
state: state.to_string(),
output_types,
lint_finding_types,
}
})
.collect()
}

pub(in crate::knowledge) fn section_state(
before: Option<&KnowledgePageSection>,
after: &KnowledgePageSectionResponse,
output_types: &[String],
) -> String {
if output_types.iter().any(|kind| kind == "missing_citation") {
return "blocked".to_string();
}
if before.is_some_and(|section| section.content_hash != after.content_hash)
|| output_types.iter().any(|kind| kind == "changed_claim" || kind == "conflict")
{
return "changed".to_string();
}

if output_types.iter().any(|kind| kind == "stale_section") {
return "stale".to_string();
}

"unchanged".to_string()
}

pub(in crate::knowledge) fn successful_rebuild_state(
diff: Option<&Value>,
outputs: &[KnowledgePageRebuildOutput],
) -> String {
if diff_content_changed(diff) {
return "changed".to_string();
}

if outputs.iter().any(|output| output.output_type == "stale_section") {
return "stale".to_string();
}

"unchanged".to_string()
}

pub(in crate::knowledge) fn section_lookup(
sections: &[KnowledgePageSection],
) -> BTreeMap<Uuid, (String, String)> {
Expand All @@ -276,13 +175,6 @@ pub(in crate::knowledge) fn diff_section_keys(diff: Option<&Value>, key: &str) -
.unwrap_or_default()
}

pub(in crate::knowledge) fn diff_content_changed(diff: Option<&Value>) -> bool {
diff.and_then(|value| value.get("content_changed")).and_then(Value::as_bool).unwrap_or(false)
|| !diff_section_keys(diff, "added_section_keys").is_empty()
|| !diff_section_keys(diff, "removed_section_keys").is_empty()
|| !diff_section_keys(diff, "changed_section_keys").is_empty()
}

pub(in crate::knowledge) fn changed_source_set(
changed_sources: &[KnowledgePageChangedSource],
) -> BTreeSet<(String, Uuid)> {
Expand All @@ -303,42 +195,6 @@ pub(in crate::knowledge) fn output_section_keys(
.collect()
}

pub(in crate::knowledge) fn outputs_by_section(
outputs: &[KnowledgePageRebuildOutput],
) -> BTreeMap<&str, Vec<String>> {
let mut map = BTreeMap::<&str, Vec<String>>::new();

for output in outputs {
let Some(section_key) = output.section_key.as_deref() else {
continue;
};

map.entry(section_key).or_default().push(output.output_type.clone());
}
for values in map.values_mut() {
values.sort();
values.dedup();
}

map
}

pub(in crate::knowledge) fn lint_finding_types_for_outputs(output_types: &[String]) -> Vec<String> {
let mut out = output_types
.iter()
.filter_map(|output_type| match output_type.as_str() {
"stale_section" => Some("stale_source_ref".to_string()),
"missing_citation" => Some("missing_citation".to_string()),
_ => None,
})
.collect::<Vec<_>>();

out.sort();
out.dedup();

out
}

pub(in crate::knowledge) fn candidate_reasons_by_section(
outputs: &[KnowledgePageRebuildOutput],
) -> BTreeMap<&str, String> {
Expand Down
146 changes: 146 additions & 0 deletions packages/elf-service/src/knowledge/watch/states.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use crate::knowledge::watch::{
BTreeMap, KnowledgePageRebuildOutput, KnowledgePageSection, KnowledgePageSectionRebuildState,
KnowledgePageSectionResponse, Value, outputs,
};

pub(in crate::knowledge) fn successful_section_states(
before_sections: &[KnowledgePageSection],
rebuilt_sections: &[KnowledgePageSectionResponse],
outputs: &[KnowledgePageRebuildOutput],
) -> Vec<KnowledgePageSectionRebuildState> {
let output_map = outputs_by_section(outputs);
let before_by_key = before_sections
.iter()
.map(|section| (section.section_key.as_str(), section))
.collect::<BTreeMap<_, _>>();

rebuilt_sections
.iter()
.map(|section| {
let output_types =
output_map.get(section.section_key.as_str()).cloned().unwrap_or_default();
let lint_finding_types = lint_finding_types_for_outputs(&output_types);
let state = section_state(
before_by_key.get(section.section_key.as_str()).copied(),
section,
&output_types,
);

KnowledgePageSectionRebuildState {
section_key: section.section_key.clone(),
heading: section.heading.clone(),
state,
output_types,
lint_finding_types,
}
})
.collect()
}

pub(in crate::knowledge) fn blocked_section_states(
sections: &[KnowledgePageSection],
outputs: &[KnowledgePageRebuildOutput],
) -> Vec<KnowledgePageSectionRebuildState> {
let output_map = outputs_by_section(outputs);

sections
.iter()
.map(|section| {
let output_types =
output_map.get(section.section_key.as_str()).cloned().unwrap_or_default();
let lint_finding_types = lint_finding_types_for_outputs(&output_types);
let state = if output_types.iter().any(|kind| kind == "missing_citation") {
"blocked"
} else if output_types.iter().any(|kind| kind == "stale_section") {
"stale"
} else {
"blocked"
};

KnowledgePageSectionRebuildState {
section_key: section.section_key.clone(),
heading: section.heading.clone(),
state: state.to_string(),
output_types,
lint_finding_types,
}
})
.collect()
}

pub(in crate::knowledge) fn successful_rebuild_state(
diff: Option<&Value>,
outputs: &[KnowledgePageRebuildOutput],
) -> String {
if diff_content_changed(diff) {
return "changed".to_string();
}

if outputs.iter().any(|output| output.output_type == "stale_section") {
return "stale".to_string();
}

"unchanged".to_string()
}

fn section_state(
before: Option<&KnowledgePageSection>,
after: &KnowledgePageSectionResponse,
output_types: &[String],
) -> String {
if output_types.iter().any(|kind| kind == "missing_citation") {
return "blocked".to_string();
}
if before.is_some_and(|section| section.content_hash != after.content_hash)
|| output_types.iter().any(|kind| kind == "changed_claim" || kind == "conflict")
{
return "changed".to_string();
}

if output_types.iter().any(|kind| kind == "stale_section") {
return "stale".to_string();
}

"unchanged".to_string()
}

fn diff_content_changed(diff: Option<&Value>) -> bool {
diff.and_then(|value| value.get("content_changed")).and_then(Value::as_bool).unwrap_or(false)
|| !outputs::diff_section_keys(diff, "added_section_keys").is_empty()
|| !outputs::diff_section_keys(diff, "removed_section_keys").is_empty()
|| !outputs::diff_section_keys(diff, "changed_section_keys").is_empty()
}

fn outputs_by_section(outputs: &[KnowledgePageRebuildOutput]) -> BTreeMap<&str, Vec<String>> {
let mut map = BTreeMap::<&str, Vec<String>>::new();

for output in outputs {
let Some(section_key) = output.section_key.as_deref() else {
continue;
};

map.entry(section_key).or_default().push(output.output_type.clone());
}
for values in map.values_mut() {
values.sort();
values.dedup();
}

map
}

fn lint_finding_types_for_outputs(output_types: &[String]) -> Vec<String> {
let mut out = output_types
.iter()
.filter_map(|output_type| match output_type.as_str() {
"stale_section" => Some("stale_source_ref".to_string()),
"missing_citation" => Some("missing_citation".to_string()),
_ => None,
})
.collect::<Vec<_>>();

out.sort();
out.dedup();

out
}