diff --git a/Cargo.lock b/Cargo.lock index b9ca793..52dfdbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -287,6 +287,21 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.94" @@ -687,6 +702,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + [[package]] name = "ciborium" version = "0.2.2" @@ -737,6 +766,7 @@ version = "0.1.3" dependencies = [ "actix-files", "actix-web", + "chrono", "console_error_panic_hook", "futures", "gloo-net 0.5.0", @@ -831,6 +861,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" version = "0.2.16" @@ -1383,6 +1419,29 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -2124,6 +2183,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.36.5" @@ -3507,6 +3575,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 3794533..978b3b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ serde_json="1.0.133" thiserror = "2.0.9" zerofrom = "0.1" mio = "0.8" +chrono = "0.4" [features] default = ["ssr"] diff --git a/src/components/items_list.rs b/src/components/items_list.rs index cb7a966..51d558e 100644 --- a/src/components/items_list.rs +++ b/src/components/items_list.rs @@ -8,6 +8,7 @@ use crate::models::item::Item; use std::collections::HashMap; use std::sync::Arc; use wasm_bindgen::JsCast; +use chrono::{DateTime, Utc}; #[derive(Deserialize, Clone, Debug)] struct WikidataSuggestion { @@ -299,10 +300,68 @@ pub fn ItemsList( }); }; + // function to handle different nested JSON types for property values + async fn parse_property_value(value: &serde_json::Value) -> String { + match value { + serde_json::Value::String(text) => text.clone(), + serde_json::Value::Number(num) => num.to_string(), + serde_json::Value::Object(map) => { + + // Handle time values + if let Some(time_value) = map.get("time") { + let precision = map.get("precision").and_then(|p| p.as_u64()).unwrap_or(11); + + if let Some(time_str) = time_value.as_str() { + if let Ok(parsed_date) = chrono::DateTime::parse_from_rfc3339(time_str.trim_start_matches('+')) { + return match precision { + 9 => parsed_date.format("%Y").to_string(), // Year precision + 10 => parsed_date.format("%Y-%m").to_string(), // Month precision + 11 => parsed_date.format("%Y-%m-%d").to_string(), // Day precision + _ => parsed_date.format("%Y-%m-%d %H:%M:%S").to_string(), + }; + } + } + return "Invalid time format".to_string(); + } + + // Handle Wikidata entity references + if let Some(id) = map.get("id") { + // Handle Wikidata entity references + let entity_id = id.as_str().unwrap_or(""); + if entity_id.starts_with("Q") { + return fetch_entity_label(entity_id).await; + } + } + serde_json::to_string(map).unwrap_or("Complex Object".to_string()) + } + _ => "Unsupported data type".to_string(), + } + } + + async fn fetch_entity_label(entity_id: &str) -> String { + let url = format!( + "https://www.wikidata.org/w/api.php?action=wbgetentities&ids={}&props=labels&languages=en&format=json&origin=*", + entity_id + ); + + match gloo_net::http::Request::get(&url).send().await { + Ok(response) => { + if let Ok(data) = response.json::().await { + if let Some(entity) = data["entities"][entity_id]["labels"]["en"]["value"].as_str() { + return entity.to_string(); + } + } + } + Err(err) => log!("Error fetching entity label: {:?}", err), + } + + entity_id.to_string() // Fallback to entity ID if label fetch fails + } + //function to fetch properties async fn fetch_item_properties(wikidata_id: &str, set_fetched_properties: WriteSignal>, set_property_labels: WriteSignal>,) -> HashMap { let url = format!( - "https://www.wikidata.org/wiki/Special:EntityData/{}.json", + "https://www.wikidata.org/w/api.php?action=wbgetentities&ids={}&format=json&props=claims&origin=*", wikidata_id ); @@ -313,17 +372,16 @@ pub fn ItemsList( if let Some(entity) = entities.get(wikidata_id) { if let Some(claims) = entity["claims"].as_object() { let mut result = HashMap::new(); + for (property, values) in claims { - if let Some(value) = values[0]["mainsnak"]["datavalue"]["value"].as_str() { - result.insert(property.clone(), value.to_string()); - } else if let Some(value) = values[0]["mainsnak"]["datavalue"]["value"].as_object() { - result.insert(property.clone(), serde_json::to_string(value).unwrap()); - } else if let Some(value) = values[0]["mainsnak"]["datavalue"]["value"].as_f64() { - result.insert(property.clone(), value.to_string()); - } else { - result.insert(property.clone(), "Unsupported data type".to_string()); + for value_entry in values.as_array().unwrap_or(&vec![]) { + if let Some(datavalue) = value_entry["mainsnak"]["datavalue"].get("value") { + let parsed_value = parse_property_value(datavalue).await; + result.insert(property.clone(), parsed_value); + } } } + // Fetch labels for the properties let property_ids = result.keys().cloned().collect::>(); let labels = fetch_property_labels(property_ids).await;