BUS-1453: Implement color fallback logic for metric handlers

Co-Authored-By: Dallin Bentley <dallinbentley98@gmail.com>
This commit is contained in:
Devin AI 2025-07-18 20:50:33 +00:00
parent 2de702f425
commit d5beb5edeb
4 changed files with 141 additions and 3 deletions

View File

@ -0,0 +1,125 @@
use anyhow::{anyhow, Result};
use database::pool::get_pg_pool;
use diesel::{ExpressionMethods, QueryDsl};
use diesel_async::RunQueryDsl;
use serde_json::Value;
use uuid::Uuid;
pub const DEFAULT_COLOR_PALETTE: [&str; 10] = [
"#B399FD", "#FC8497", "#FBBC30", "#279EFF",
"#E83562", "#41F8FF", "#F3864F", "#C82184",
"#31FCB4", "#E83562"
];
pub async fn get_organization_color_palette(organization_id: &Uuid) -> Result<Option<Vec<String>>> {
let mut conn = get_pg_pool().get().await?;
let config_result = diesel::sql_query(
"SELECT config FROM users
INNER JOIN users_to_organizations ON users.id = users_to_organizations.user_id
WHERE users_to_organizations.organization_id = $1
AND users_to_organizations.deleted_at IS NULL
LIMIT 1"
)
.bind::<diesel::sql_types::Uuid, _>(organization_id)
.get_result::<ConfigResult>(&mut conn)
.await;
match config_result {
Ok(config_row) => {
if let Some(config_value) = config_row.config {
if let Some(selected_palette) = config_value.get("selectedDictionaryPalette") {
if let Some(colors) = selected_palette.get("colors") {
if let Some(colors_array) = colors.as_array() {
let palette: Vec<String> = colors_array
.iter()
.filter_map(|c| c.as_str().map(|s| s.to_string()))
.collect();
if !palette.is_empty() {
return Ok(Some(palette));
}
}
}
}
if let Some(last_used) = config_value.get("last_used_color_palette") {
if let Some(colors_array) = last_used.as_array() {
let palette: Vec<String> = colors_array
.iter()
.filter_map(|c| c.as_str().map(|s| s.to_string()))
.collect();
if !palette.is_empty() {
return Ok(Some(palette));
}
}
}
}
Ok(None)
},
Err(_) => {
Ok(None)
}
}
}
pub fn get_default_color_palette() -> Vec<String> {
DEFAULT_COLOR_PALETTE.iter().map(|&s| s.to_string()).collect()
}
pub async fn apply_color_fallback(
chart_config: &mut database::types::ChartConfig,
organization_id: &Uuid,
) -> Result<()> {
let colors_already_set = match chart_config {
database::types::ChartConfig::Bar(config) => config.base.colors.is_some(),
database::types::ChartConfig::Line(config) => config.base.colors.is_some(),
database::types::ChartConfig::Scatter(config) => config.base.colors.is_some(),
database::types::ChartConfig::Pie(config) => config.base.colors.is_some(),
database::types::ChartConfig::Combo(config) => config.base.colors.is_some(),
database::types::ChartConfig::Metric(_) => true, // Metric doesn't use colors
database::types::ChartConfig::Table(_) => true, // Table doesn't use colors
};
if colors_already_set {
return Ok(());
}
let org_palette = get_organization_color_palette(organization_id).await?;
let palette = if let Some(palette) = org_palette {
palette
} else {
get_default_color_palette()
};
match chart_config {
database::types::ChartConfig::Bar(config) => {
config.base.colors = Some(palette);
},
database::types::ChartConfig::Line(config) => {
config.base.colors = Some(palette);
},
database::types::ChartConfig::Scatter(config) => {
config.base.colors = Some(palette);
},
database::types::ChartConfig::Pie(config) => {
config.base.colors = Some(palette);
},
database::types::ChartConfig::Combo(config) => {
config.base.colors = Some(palette);
},
database::types::ChartConfig::Metric(_) => {}, // No colors to set
database::types::ChartConfig::Table(_) => {}, // No colors to set
}
Ok(())
}
#[derive(diesel::QueryableByName, Debug)]
struct ConfigResult {
#[diesel(sql_type = diesel::sql_types::Jsonb)]
config: Option<Value>,
}

View File

@ -9,6 +9,7 @@ use serde_yaml;
use sharing::asset_access_checks::check_metric_collection_access;
use uuid::Uuid;
use crate::metrics::color_palette_helpers::apply_color_fallback;
use crate::metrics::types::{AssociatedCollection, AssociatedDashboard, BusterMetric, BusterShareIndividual, Dataset};
use crate::utils::workspace::count_workspace_members;
use database::enums::{AssetPermissionRole, AssetType, IdentityType};
@ -448,6 +449,11 @@ pub async fn get_metric_for_dashboard_handler(
}
};
let mut final_chart_config = resolved_chart_config.clone();
if let Err(e) = apply_color_fallback(&mut final_chart_config, &metric_file.organization_id).await {
tracing::warn!(metric_id = %metric_id, error = %e, "Failed to apply color fallback logic, continuing with original chart config");
}
// Get workspace sharing enabled by email if set
let workspace_sharing_enabled_by = if let Some(enabled_by_id) = metric_file.workspace_sharing_enabled_by {
users::table
@ -488,7 +494,7 @@ pub async fn get_metric_for_dashboard_handler(
datasets,
data_source_id: metric_file.data_source_id,
error: None,
chart_config: Some(resolved_chart_config),
chart_config: Some(final_chart_config), // Use chart config with color fallback applied
data_metadata,
status: metric_file.verification,
evaluation_score,
@ -517,4 +523,4 @@ pub async fn get_metric_for_dashboard_handler(
// Workspace member count
workspace_member_count,
})
}
}

View File

@ -7,6 +7,7 @@ use serde_yaml;
use sharing::asset_access_checks::check_metric_collection_access;
use uuid::Uuid;
use crate::metrics::color_palette_helpers::apply_color_fallback;
use crate::metrics::types::{AssociatedCollection, AssociatedDashboard, BusterMetric, Dataset};
use crate::utils::workspace::count_workspace_members;
use database::enums::{AssetPermissionRole, AssetType, IdentityType};
@ -304,6 +305,11 @@ pub async fn get_metric_handler(
tracing::debug!(metric_id = %metric_id, latest_version = resolved_version_num, "Determined latest version number");
}
let mut final_chart_config = resolved_chart_config.clone();
if let Err(e) = apply_color_fallback(&mut final_chart_config, &metric_file.organization_id).await {
tracing::warn!(metric_id = %metric_id, error = %e, "Failed to apply color fallback logic, continuing with original chart config");
}
// Convert the selected content to pretty YAML for the 'file' field
let file = match serde_yaml::to_string(&resolved_content_for_yaml) {
Ok(yaml) => yaml,
@ -494,7 +500,7 @@ pub async fn get_metric_handler(
datasets, // Fetched based on resolved_dataset_ids (for display purposes only)
data_source_id: metric_file.data_source_id, // Use canonical ID (Uuid) from main record
error: None, // Assume ok
chart_config: Some(resolved_chart_config), // Use resolved chart config
chart_config: Some(final_chart_config), // Use chart config with color fallback applied
data_metadata, // Not versioned
status: metric_file.verification, // Not versioned
evaluation_score, // Not versioned

View File

@ -1,4 +1,5 @@
pub mod bulk_update_metrics_handler;
pub mod color_palette_helpers;
pub mod delete_metric_handler;
pub mod get_metric_data_handler;
pub mod get_metric_handler;