diff --git a/src/area/service.rs b/src/area/service.rs index 76b80c7..8d376d0 100644 --- a/src/area/service.rs +++ b/src/area/service.rs @@ -1,5 +1,3 @@ -use std::collections::{HashMap, HashSet}; - use super::Area; use crate::{ area_element::model::AreaElement, @@ -12,13 +10,13 @@ use geojson::GeoJson; use rusqlite::Connection; use serde::Serialize; use serde_json::{Map, Value}; +use std::collections::{HashMap, HashSet}; use time::OffsetDateTime; -pub fn insert(tags: Map, conn: &mut Connection) -> Result { +pub fn insert(tags: Map, conn: &Connection) -> Result { if !tags.contains_key("geo_json") { return Err(Error::HttpBadRequest("geo_json tag is missing".into())); } - let sp = conn.savepoint()?; let url_alias = tags .get("url_alias") .ok_or(Error::HttpBadRequest( @@ -41,15 +39,14 @@ pub fn insert(tags: Map, conn: &mut Connection) -> Result { if geo_json.is_err() { Err(Error::HttpConflict("Invalid geo_json".into()))? } - if Area::select_by_alias(url_alias, &sp)?.is_some() { + if Area::select_by_alias(url_alias, &conn)?.is_some() { Err(Error::HttpConflict( "This url_alias is already in use".into(), ))? } - let area = Area::insert(tags, &sp)?; - let area_elements = element::service::find_in_area(&area, &sp)?; - element::service::update_areas_tag(&area_elements, &sp)?; - sp.commit()?; + let area = Area::insert(tags, &conn)?; + let area_elements = element::service::find_in_area(&area, conn)?; + element::service::generate_areas_mapping_old(&area_elements, conn)?; Ok(area) } @@ -64,20 +61,14 @@ pub fn patch_tag( patch_tags(id_or_alias, tags, conn) } -pub fn patch_tags( - id_or_alias: &str, - tags: Map, - conn: &mut Connection, -) -> Result { +pub fn patch_tags(id_or_alias: &str, tags: Map, conn: &Connection) -> Result { let area = Area::select_by_id_or_alias(id_or_alias, conn)?.unwrap(); if tags.contains_key("geo_json") { - let sp = conn.savepoint()?; - let area_elements = element::service::find_in_area(&area, &sp)?; - element::service::update_areas_tag(&area_elements, &sp)?; - let area = Area::patch_tags(area.id, tags, &sp)?; - let area_elements = element::service::find_in_area(&area, &sp)?; - element::service::update_areas_tag(&area_elements, &sp)?; - sp.commit()?; + let area_elements = element::service::find_in_area(&area, conn)?; + let area = Area::patch_tags(area.id, tags, conn)?; + element::service::generate_areas_mapping_old(&area_elements, conn)?; + let area_elements = element::service::find_in_area(&area, conn)?; + element::service::generate_areas_mapping_old(&area_elements, conn)?; Ok(area) } else { Ok(Area::patch_tags(area.id, tags, conn)?) @@ -94,13 +85,11 @@ pub fn remove_tag(area_id_or_alias: &str, tag_name: &str, conn: &mut Connection) Ok(Area::remove_tag(area.id, tag_name, conn)?) } -pub fn soft_delete(area_id_or_alias: &str, conn: &mut Connection) -> Result { - let sp = conn.savepoint()?; - let area = Area::select_by_id_or_alias(area_id_or_alias, &sp)?.unwrap(); - let area_elements = element::service::find_in_area(&area, &sp)?; - let area = Area::set_deleted_at(area.id, Some(OffsetDateTime::now_utc()), &sp)?; - element::service::update_areas_tag(&area_elements, &sp)?; - sp.commit()?; +pub fn soft_delete(area_id_or_alias: &str, conn: &Connection) -> Result { + let area = Area::select_by_id_or_alias(area_id_or_alias, conn)?.unwrap(); + let area_elements = element::service::find_in_area(&area, conn)?; + let area = Area::set_deleted_at(area.id, Some(OffsetDateTime::now_utc()), conn)?; + element::service::generate_areas_mapping_old(&area_elements, conn)?; Ok(area) } diff --git a/src/area_element/mod.rs b/src/area_element/mod.rs index 65880be..908db95 100644 --- a/src/area_element/mod.rs +++ b/src/area_element/mod.rs @@ -1 +1,2 @@ pub mod model; +pub mod service; diff --git a/src/area_element/service.rs b/src/area_element/service.rs new file mode 100644 index 0000000..6e444ac --- /dev/null +++ b/src/area_element/service.rs @@ -0,0 +1,37 @@ +use super::model::AreaElement; +use crate::{area::Area, element::Element}; +use crate::{element, Result}; +use rusqlite::Connection; +use time::OffsetDateTime; + +pub struct Res { + pub has_changes: bool, +} + +pub fn generate_areas_mapping( + element: &Element, + areas: &Vec, + conn: &Connection, +) -> Result { + let mut has_changes = false; + let element_areas = element::service::find_areas(&element, &areas)?; + let old_mappings = AreaElement::select_by_element_id(element.id, conn)?; + let mut old_area_ids: Vec = old_mappings.into_iter().map(|it| it.area_id).collect(); + let mut new_area_ids: Vec = element_areas.into_iter().map(|it| it.id).collect(); + old_area_ids.sort(); + new_area_ids.sort(); + if new_area_ids != old_area_ids { + for old_area_id in &old_area_ids { + if !new_area_ids.contains(&old_area_id) { + AreaElement::set_deleted_at(*old_area_id, Some(OffsetDateTime::now_utc()), conn)?; + } + } + for new_area_id in new_area_ids { + if !old_area_ids.contains(&new_area_id) { + AreaElement::insert(new_area_id, element.id, conn)?; + } + } + has_changes = true; + } + Ok(Res { has_changes }) +} diff --git a/src/command/sync.rs b/src/command/sync.rs index 8e40a5c..74283de 100644 --- a/src/command/sync.rs +++ b/src/command/sync.rs @@ -195,7 +195,7 @@ async fn process_elements(fresh_elements: Vec, mut db: Connecti } element::service::generate_issues(vec![&updated_element], &tx)?; - element::service::update_areas_tag(&vec![updated_element], &tx)?; + element::service::generate_areas_mapping_old(&vec![updated_element], &tx)?; } if cached_element.deleted_at.is_some() { @@ -235,7 +235,7 @@ async fn process_elements(fresh_elements: Vec, mut db: Connecti info!(category, android_icon); element::service::generate_issues(vec![&element], &tx)?; - element::service::update_areas_tag(&vec![element], &tx)?; + element::service::generate_areas_mapping_old(&vec![element], &tx)?; let message = format!("User {user_display_name} added https://www.openstreetmap.org/{element_type}/{osm_id}"); info!( diff --git a/src/command/update_areas_tag.rs b/src/command/update_areas_tag.rs index 6ef89f7..8cb5c05 100644 --- a/src/command/update_areas_tag.rs +++ b/src/command/update_areas_tag.rs @@ -11,6 +11,6 @@ pub fn run(args: Vec) -> Result<()> { let area = Area::select_by_id_or_alias(area_id_or_alias, &conn)?.unwrap(); info!(area.id, area_name = area.name(), area_alias = area.alias()); let area_elements = element::service::find_in_area(&area, &conn)?; - element::service::update_areas_tag(&area_elements, &conn)?; + element::service::generate_areas_mapping_old(&area_elements, &conn)?; Ok(()) } diff --git a/src/element/service.rs b/src/element/service.rs index 41aae68..d4c166e 100644 --- a/src/element/service.rs +++ b/src/element/service.rs @@ -1,5 +1,6 @@ use super::Element; use crate::area::Area; +use crate::area_element; use crate::Result; use geo::Contains; use geo::LineString; @@ -82,18 +83,15 @@ pub fn filter_by_area(all_elements: &Vec, area: &Area) -> Result, conn: &Connection) -> Result> { +pub fn generate_areas_mapping_old( + elements: &Vec, + conn: &Connection, +) -> Result> { let mut res: Vec = vec![]; - - let all_areas: Vec = Area::select_all(None, &conn)? - .into_iter() - .filter(|it| it.deleted_at == None) - .collect(); - + let all_areas: Vec = Area::select_all(None, &conn)?; for element in elements { let element_areas = find_areas(element, &all_areas)?; let element_areas = areas_to_areas_tag(element_areas); - let element = if element.tag("areas") != &element_areas { info!( element = element.id, @@ -106,10 +104,9 @@ pub fn update_areas_tag(elements: &Vec, conn: &Connection) -> Result = old_mappings.into_iter().map(|it| it.area_id).collect(); - let mut new_area_ids: Vec = element_areas.into_iter().map(|it| it.id).collect(); - old_area_ids.sort(); - new_area_ids.sort(); - let sp = conn.savepoint()?; - if new_area_ids != old_area_ids { - for old_area_id in &old_area_ids { - if !new_area_ids.contains(&old_area_id) { - AreaElement::set_deleted_at( - *old_area_id, - Some(OffsetDateTime::now_utc()), - &sp, - )?; - } - } - for new_area_id in new_area_ids { - if !old_area_ids.contains(&new_area_id) { - AreaElement::insert(new_area_id, element_id, &sp)?; - } - } + if area_element::service::generate_areas_mapping(&element, &areas, conn)?.has_changes { elements_affected += 1; } - sp.commit()?; elements_processed += 1; } Ok(Res {