fix(properties): correct property saving and fetching

This commit is contained in:
ryan 2025-02-26 02:18:32 +03:00
parent 7e288b3a82
commit 5a14111db7
4 changed files with 209 additions and 10 deletions

View file

@ -151,4 +151,32 @@ pub async fn delete_property_by_url(
HttpResponse::InternalServerError().body("Failed to delete property by URL")
}
}
}
#[cfg(feature = "ssr")]
pub async fn get_selected_properties(
db: web::Data<Arc<Mutex<Database>>>,
url: web::Path<String>,
) -> HttpResponse {
let db = db.lock().await;
match db.get_selected_properties(&url).await {
Ok(properties) => HttpResponse::Ok().json(properties),
Err(e) => HttpResponse::InternalServerError().body(e.to_string())
}
}
#[cfg(feature = "ssr")]
pub async fn add_selected_property(
db: web::Data<Arc<Mutex<Database>>>,
url: web::Path<String>,
property: web::Json<String>,
) -> HttpResponse {
let url = url.into_inner();
let property = property.into_inner();
let db = db.lock().await;
match db.add_selected_property(&url, &property).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string())
}
}

View file

@ -47,7 +47,60 @@ pub async fn load_items_from_db(current_url: &str) -> Result<Vec<Item>, String>
format!("Failed to parse items: {:?}", err)
})?;
log!("[DEBUG] Successfully parsed {} items", items.len());
Ok(items)
// Get the selected properties for the current URL
let selected_properties_response = gloo_net::http::Request::get(
&format!("/api/urls/{}/properties", encoded_url)
)
.send()
.await
.map_err(|err| {
log!("[ERROR] Network error: {:?}", err);
format!("Failed to fetch selected properties: {:?}", err)
})?;
if selected_properties_response.status() == 200 {
let selected_properties: Vec<String> = selected_properties_response
.json()
.await
.map_err(|err| {
log!("[ERROR] JSON parsing error: {:?}", err);
format!("Failed to parse selected properties: {:?}", err)
})?;
log!("[DEBUG] Successfully received selected properties");
// Filter the items to only include the selected properties
let filtered_items = items
.into_iter()
.map(|item| {
let filtered_custom_properties = item
.custom_properties
.into_iter()
.filter(|(key, _)| selected_properties.contains(key))
.collect();
Item {
id: item.id,
name: item.name,
description: item.description,
wikidata_id: item.wikidata_id,
custom_properties: filtered_custom_properties,
}
})
.collect();
Ok(filtered_items)
} else {
let body = selected_properties_response.text().await.unwrap_or_default();
log!("[ERROR] Server error details:
Status: {}
URL: {}
Response Body: {}
Request URL: {}",
selected_properties_response.status(),
api_url,
body,
current_url
);
Err(format!("Server error ({}): {}", selected_properties_response.status(), body))
}
} else {
let body = response.text().await.unwrap_or_default();
log!("[ERROR] Server error details:
@ -465,7 +518,44 @@ pub fn ItemsList(
Arc::new(move |property: String| {
// Normalize the property ID
let normalized_property = property.replace("http://www.wikidata.org/prop/", "");
let normalized_property_clone = normalized_property.clone();
// Check if property is already selected
if !selected_properties.get().contains_key(&normalized_property) && !normalized_property.is_empty() {
// Add property to selected properties
set_selected_properties.update(|selected| {
selected.insert(normalized_property.clone(), true);
});
// Save the selected property to the database
spawn_local({
let current_url = Rc::clone(&current_url);
let normalized_property = normalized_property_clone.clone();
async move {
let response = gloo_net::http::Request::post(
&format!("/api/urls/{}/properties", encode(&current_url))
)
.json(&normalized_property)
.unwrap()
.send()
.await;
match response {
Ok(resp) => {
if resp.status() == 200 {
log!("Property saved successfully");
} else {
log!("Error saving property: {}", resp.status_text());
}
}
Err(err) => {
log!("Error saving property: {:?}", err);
}
}
}
});
}
set_custom_properties.update(|props| {
if !props.contains(&normalized_property) && !normalized_property.is_empty() {
props.push(normalized_property.clone());

View file

@ -67,8 +67,22 @@ mod db_impl {
eprintln!("Failed creating items table: {}", e);
e
})?;
// 4. Table for selected properties
conn.execute_batch(
"CREATE TABLE IF NOT EXISTS selected_properties (
url_id INTEGER NOT NULL,
property_id INTEGER NOT NULL,
PRIMARY KEY (url_id, property_id),
FOREIGN KEY (url_id) REFERENCES urls(id) ON DELETE CASCADE,
FOREIGN KEY (property_id) REFERENCES properties(id) ON DELETE CASCADE
);"
).map_err(|e| {
eprintln!("Failed creating properties table: {}", e);
e
})?;
// 4. Junction table for custom properties
// 5. Junction table for custom properties
conn.execute_batch(
"CREATE TABLE IF NOT EXISTS item_properties (
item_id TEXT NOT NULL,
@ -373,6 +387,54 @@ mod db_impl {
tx.commit()?;
Ok(())
}
pub async fn add_selected_property(&self, url: &str, property: &str) -> Result<(), Error> {
let mut conn = self.conn.lock().await;
let tx = conn.transaction()?;
// Get URL ID
let url_id = tx.query_row(
"SELECT id FROM urls WHERE url = ?",
[url],
|row| row.get::<_, i64>(0)
)?;
// Get/Create property
let prop_id = match tx.query_row(
"SELECT id FROM properties WHERE name = ?",
[property],
|row| row.get::<_, i64>(0)
) {
Ok(id) => id,
Err(_) => {
tx.execute("INSERT INTO properties (name) VALUES (?)", [property])?;
tx.last_insert_rowid()
}
};
// Insert into selected_properties
tx.execute(
"INSERT OR IGNORE INTO selected_properties (url_id, property_id) VALUES (?, ?)",
[url_id, prop_id]
)?;
tx.commit()?;
Ok(())
}
pub async fn get_selected_properties(&self, url: &str) -> Result<Vec<String>, Error> {
let conn = self.conn.lock().await;
let mut stmt = conn.prepare(
"SELECT p.name
FROM selected_properties sp
JOIN properties p ON sp.property_id = p.id
JOIN urls u ON sp.url_id = u.id
WHERE u.url = ?"
)?;
let properties = stmt.query_map([url], |row| row.get(0))?;
properties.collect()
}
// function to log database state
pub async fn debug_dump(&self) -> Result<(), Error> {

View file

@ -3,7 +3,7 @@ use actix_web::{web, HttpResponse, Responder};
use std::sync::Arc;
use tokio::sync::Mutex;
use compareware::db::Database;
use compareware::api::{ItemRequest,create_item, get_items, delete_item_by_url};
use compareware::api::{ItemRequest,create_item, get_items, get_selected_properties, add_selected_property};
use compareware::models::item::Item;
#[actix_web::main]
@ -14,7 +14,7 @@ async fn main() -> std::io::Result<()> {
use leptos_actix::{generate_route_list, LeptosRoutes};
use compareware::app::*;
use compareware::db::Database;
use compareware::api::{get_items, create_item, delete_item, delete_property}; // Import API handlers
use compareware::api::{delete_item, delete_property}; // Import API handlers
use std::sync::Arc;
use tokio::sync::Mutex;
@ -50,6 +50,8 @@ async fn main() -> std::io::Result<()> {
.route("/items", web::get().to(get_items_handler)) // GET items by URL
.route("/items", web::post().to(create_item_handler)) // Create item for URL
.route("/items/{item_id}", web::delete().to(delete_item)) // Delete item for URL
.route("/properties", web::get().to(get_selected_properties_handler))
.route("/properties", web::post().to(add_selected_property_handler))
.route("/properties/{property}", web::delete().to(delete_property)) // Delete property for URL
)
)
@ -98,13 +100,30 @@ async fn create_item_handler(
create_item(db, web::Json(request)).await
}
// Handler to delete an item for a specific URL
async fn delete_item_handler(
// // Handler to delete an item for a specific URL
// async fn delete_item_handler(
// db: web::Data<Arc<Mutex<Database>>>,
// path: web::Path<(String, String)>,
// ) -> impl Responder {
// let (url, item_id) = path.into_inner();
// delete_item_by_url(db, web::Path::from(url), web::Path::from(item_id)).await
// }
#[cfg(feature = "ssr")]
async fn get_selected_properties_handler(
db: web::Data<Arc<Mutex<Database>>>,
path: web::Path<(String, String)>,
url: web::Path<String>,
) -> impl Responder {
let (url, item_id) = path.into_inner();
delete_item_by_url(db, web::Path::from(url), web::Path::from(item_id)).await
get_selected_properties(db, url).await
}
#[cfg(feature = "ssr")]
async fn add_selected_property_handler(
db: web::Data<Arc<Mutex<Database>>>,
url: web::Path<String>,
property: web::Json<String>,
) -> impl Responder {
add_selected_property(db, url, property).await
}
#[cfg(feature = "ssr")]
// Define the index handler