From 0c01eb93057f1abd600199567b840da1e9ee1c67 Mon Sep 17 00:00:00 2001 From: dal Date: Fri, 18 Apr 2025 14:20:41 -0600 Subject: [PATCH] restore chat --- api/libs/database/src/enums.rs | 13 ++ api/libs/database/src/types/metric_yml.rs | 113 ++++++++++++++++-- .../src/chats/restore_chat_handler.rs | 45 ++++--- 3 files changed, 136 insertions(+), 35 deletions(-) diff --git a/api/libs/database/src/enums.rs b/api/libs/database/src/enums.rs index 0a0e19307..0bc41308d 100644 --- a/api/libs/database/src/enums.rs +++ b/api/libs/database/src/enums.rs @@ -228,6 +228,19 @@ pub enum AssetType { DashboardFile, } +impl AssetType { + pub fn to_string(&self) -> &'static str { + match *self { + AssetType::Dashboard => "dashboard_deprecated", + AssetType::Thread => "thread", + AssetType::Collection => "collection", + AssetType::Chat => "chat", + AssetType::MetricFile => "metric", + AssetType::DashboardFile => "dashboard", + } + } +} + #[derive( Serialize, Deserialize, diff --git a/api/libs/database/src/types/metric_yml.rs b/api/libs/database/src/types/metric_yml.rs index 98d10599e..6cb82968e 100644 --- a/api/libs/database/src/types/metric_yml.rs +++ b/api/libs/database/src/types/metric_yml.rs @@ -85,6 +85,98 @@ pub enum ShowLegendHeadline { String(String), } +// --- Axis Configuration Enums --- + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(rename_all = "camelCase")] +pub enum YAxisScaleType { + Log, + Linear, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(rename_all = "camelCase")] +pub enum XAxisTimeInterval { + Day, + Week, + Month, + Quarter, + Year, +} + +// Use strings to represent numbers and 'auto' for compatibility with TS/JSON +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub enum XAxisLabelRotation { + #[serde(rename = "0")] + Rotate0, + #[serde(rename = "45")] + Rotate45, + #[serde(rename = "90")] + Rotate90, + #[serde(rename = "auto")] + Auto, +} + + +// --- Axis Configuration Structs --- + +#[derive(Debug, Serialize, Deserialize, Clone, Default)] +#[serde(rename_all = "camelCase")] +pub struct YAxisConfig { + #[serde(skip_serializing_if = "Option::is_none", alias = "y_axis_show_axis_label")] + pub y_axis_show_axis_label: Option, + #[serde(skip_serializing_if = "Option::is_none", alias = "y_axis_show_axis_title")] + pub y_axis_show_axis_title: Option, + #[serde(skip_serializing_if = "Option::is_none", alias = "y_axis_axis_title")] + pub y_axis_axis_title: Option, + #[serde(skip_serializing_if = "Option::is_none", alias = "y_axis_start_axis_at_zero")] + pub y_axis_start_axis_at_zero: Option, + #[serde(skip_serializing_if = "Option::is_none", alias = "y_axis_scale_type")] + pub y_axis_scale_type: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone, Default)] +#[serde(rename_all = "camelCase")] +pub struct Y2AxisConfig { + #[serde(skip_serializing_if = "Option::is_none", alias = "y2_axis_show_axis_label")] + pub y2_axis_show_axis_label: Option, + #[serde(skip_serializing_if = "Option::is_none", alias = "y2_axis_show_axis_title")] + pub y2_axis_show_axis_title: Option, + #[serde(skip_serializing_if = "Option::is_none", alias = "y2_axis_axis_title")] + pub y2_axis_axis_title: Option, + #[serde(skip_serializing_if = "Option::is_none", alias = "y2_axis_start_axis_at_zero")] + pub y2_axis_start_axis_at_zero: Option, + #[serde(skip_serializing_if = "Option::is_none", alias = "y2_axis_scale_type")] + pub y2_axis_scale_type: Option, // Reuses YAxisScaleType enum +} + +#[derive(Debug, Serialize, Deserialize, Clone, Default)] +#[serde(rename_all = "camelCase")] +pub struct XAxisConfig { + #[serde(skip_serializing_if = "Option::is_none", alias = "x_axis_time_interval")] + pub x_axis_time_interval: Option, + #[serde(skip_serializing_if = "Option::is_none", alias = "x_axis_show_axis_label")] + pub x_axis_show_axis_label: Option, + #[serde(skip_serializing_if = "Option::is_none", alias = "x_axis_show_axis_title")] + pub x_axis_show_axis_title: Option, + #[serde(skip_serializing_if = "Option::is_none", alias = "x_axis_axis_title")] + pub x_axis_axis_title: Option, + #[serde(skip_serializing_if = "Option::is_none", alias = "x_axis_label_rotation")] + pub x_axis_label_rotation: Option, + #[serde(skip_serializing_if = "Option::is_none", alias = "x_axis_data_zoom")] + pub x_axis_data_zoom: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone, Default)] +#[serde(rename_all = "camelCase")] +pub struct CategoryAxisStyleConfig { + #[serde(skip_serializing_if = "Option::is_none", alias = "category_axis_title")] + pub category_axis_title: Option, +} + + +// --- Base Chart Config --- + // Base chart config shared by all chart types #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] @@ -113,18 +205,15 @@ pub struct BaseChartConfig { #[serde(skip_serializing_if = "Option::is_none")] #[serde(alias = "disable_tooltip")] pub disable_tooltip: Option, - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(alias = "y_axis_config")] - pub y_axis_config: Option, - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(alias = "x_axis_config")] - pub x_axis_config: Option, - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(alias = "category_axis_style_config")] - pub category_axis_style_config: Option, - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(alias = "y2_axis_config")] - pub y2_axis_config: Option, + // Updated Axis Configs using defined structs + #[serde(flatten, default)] // Flatten includes fields directly, default handles Option + pub y_axis_config: YAxisConfig, + #[serde(flatten, default)] + pub x_axis_config: XAxisConfig, + #[serde(flatten, default)] + pub category_axis_style_config: CategoryAxisStyleConfig, + #[serde(flatten, default)] + pub y2_axis_config: Y2AxisConfig, } #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/api/libs/handlers/src/chats/restore_chat_handler.rs b/api/libs/handlers/src/chats/restore_chat_handler.rs index 7b6329414..9f337cd34 100644 --- a/api/libs/handlers/src/chats/restore_chat_handler.rs +++ b/api/libs/handlers/src/chats/restore_chat_handler.rs @@ -4,9 +4,9 @@ use database::{ enums::AssetType, models::{Message, MessageToFile}, pool::get_pg_pool, - schema::{messages, messages_to_files}, + schema::{chats, messages, messages_to_files}, }; -use diesel::{insert_into, ExpressionMethods, QueryDsl}; +use diesel::{insert_into, update, ExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; use middleware::AuthenticatedUser; use serde::{Deserialize, Serialize}; @@ -44,7 +44,8 @@ pub struct ChatRestoreRequest { /// 1. Restores the specified asset version using the appropriate handler /// 2. Creates a text message in the chat documenting the restoration /// 3. Creates a file message linking to the restored asset -/// 4. Returns the updated chat with all messages +/// 4. Updates the chat record with the latest file info +/// 5. Returns the updated chat with all messages pub async fn restore_chat_handler( chat_id: &Uuid, user: &AuthenticatedUser, @@ -161,32 +162,15 @@ pub async fn restore_chat_handler( let now = Utc::now(); let timestamp = now.timestamp(); - // Create restoration message text response - let restoration_text = format!( - "Version {} was created by restoring version {}", - version_number, request.version_number - ); - - // Create file response message for the restored asset - // Create response messages array with both text and file response let response_messages = json!([ - // Text response message - { - "id": format!("chatcmpl-{}", Uuid::new_v4().to_string().replace("-", "")), - "type": "text", - "message": restoration_text, - "message_chunk": null, - "is_final_message": true - }, - // File response message { "id": file_id.to_string(), "type": "file", "metadata": [ { "status": "completed", - "message": format!("File {} completed", file_name), + "message": format!("Restored from version {}", request.version_number), "timestamp": timestamp } ], @@ -205,7 +189,10 @@ pub async fn restore_chat_handler( reasoning: json!([]), title: "Version Restoration".to_string(), raw_llm_messages: Value::Array(raw_llm_messages.clone()), - final_reasoning_message: None, + final_reasoning_message: Some(format!( + "v{} was created by restoring v{}", + version_number, request.version_number + )), chat_id: *chat_id, created_at: now, updated_at: now, @@ -233,11 +220,23 @@ pub async fn restore_chat_handler( }; // Insert the message-to-file association into the database - insert_into(messages_to_files::table) + diesel::insert_into(messages_to_files::table) .values(&message_to_file) .execute(&mut conn) .await?; + // Step 4: Update the chat record with the latest file info + update(chats::table) + .filter(chats::id.eq(chat_id)) + .set(( + chats::most_recent_file_id.eq(Some(file_id)), + chats::most_recent_version_number.eq(Some(version_number)), + chats::most_recent_file_type.eq(Some(request.asset_type.to_string())), + chats::updated_at.eq(now), + )) + .execute(&mut conn) + .await?; + // Return the updated chat with messages get_chat_handler(chat_id, user, false).await }