Skip to content

Commit

Permalink
feat: sort experiments in the frontend based on last_modified_at and …
Browse files Browse the repository at this point in the history
…created_at
  • Loading branch information
Datron committed Nov 26, 2024
1 parent bf1cba6 commit 94e1bd4
Show file tree
Hide file tree
Showing 16 changed files with 357 additions and 151 deletions.
50 changes: 25 additions & 25 deletions crates/experimentation_platform/src/api/experiments/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use actix_web::{
use anyhow::anyhow;
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
use diesel::{
dsl::sql,
r2d2::{ConnectionManager, PooledConnection},
sql_types::{Bool, Text},
ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl, TextExpressionMethods,
};
use reqwest::{Method, Response, StatusCode};
Expand All @@ -23,10 +25,9 @@ use service_utils::service::types::{
use superposition_macros::{bad_argument, response_error, unexpected_error};
use superposition_types::{
custom_query::PaginationParams,
custom_query::SortBy,
result::{self as superposition},
webhook::{WebhookConfig, WebhookEvent},
Condition, Exp, Overrides, TenantConfig, User,
Condition, Exp, Overrides, SortBy, TenantConfig, User,
};

use super::{
Expand Down Expand Up @@ -554,15 +555,27 @@ async fn list_experiments(
if let Some(ref states) = filters.status {
builder = builder.filter(experiments::status.eq_any(states.0.clone()));
}
if let Some(experiment_name) = filters.experiment_name.clone() {
builder = builder.filter(experiments::name.like(format!("%{}%", experiment_name)))
if let Some(ref experiment_name) = filters.experiment_name {
builder =
builder.filter(experiments::name.like(format!("%{}%", experiment_name)));
}
if let Some(ref context_search) = filters.context {
builder = builder.filter(
sql::<Bool>("context::text LIKE ")
.bind::<Text, _>(format!("%{}%", context_search)),
);
}
if let Some(ref created_by) = filters.created_by {
builder = builder.filter(experiments::created_by.eq_any(created_by.0.clone()))
builder =
builder.filter(experiments::created_by.eq_any(created_by.0.clone()));
}
if let Some(experiment_ids) = filters.experiment_ids.clone() {
let experiment_ids: Vec<i64> = experiment_ids.0.iter().map(|i| i.parse::<i64>().unwrap_or_default()).collect();
builder = builder.filter(experiments::id.eq_any(experiment_ids))
let experiment_ids: HashSet<i64> = experiment_ids
.0
.iter()
.map(|i| i.parse::<i64>().unwrap_or_default())
.collect();
builder = builder.filter(experiments::id.eq_any(experiment_ids));
}
let now = Utc::now();
builder
Expand Down Expand Up @@ -591,25 +604,12 @@ async fn list_experiments(

let sort_by = filters.sort_by.unwrap_or_default();
let sort_on = filters.sort_on.unwrap_or_default();
#[rustfmt::skip]
let base_query = match (sort_on, sort_by) {
(ExperimentSortOn::LastModifiedAt, SortBy::Desc) => {
base_query.order(experiments::last_modified.desc())
}
(ExperimentSortOn::LastModifiedAt, SortBy::Asc) => {
base_query.order(experiments::last_modified.asc())
}
(ExperimentSortOn::CreatedAt, SortBy::Desc) => {
base_query.order(experiments::created_at.desc())
}
(ExperimentSortOn::CreatedAt, SortBy::Asc) => {
base_query.order(experiments::created_at.asc())
}
(ExperimentSortOn::ExperimentId, SortBy::Desc) => {
base_query.order(experiments::id.desc())
}
(ExperimentSortOn::ExperimentId, SortBy::Asc) => {
base_query.order(experiments::id.asc())
}
(ExperimentSortOn::LastModifiedAt, SortBy::Desc) => base_query.order(experiments::last_modified.desc()),
(ExperimentSortOn::LastModifiedAt, SortBy::Asc) => base_query.order(experiments::last_modified.asc()),
(ExperimentSortOn::CreatedAt, SortBy::Desc) => base_query.order(experiments::created_at.desc()),
(ExperimentSortOn::CreatedAt, SortBy::Asc) => base_query.order(experiments::created_at.asc()),
};
let query = base_query.limit(limit).offset(offset);
let experiment_list = query.load::<Experiment>(&mut conn)?;
Expand Down
7 changes: 5 additions & 2 deletions crates/experimentation_platform/src/api/experiments/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use std::collections::HashMap;
use chrono::{DateTime, NaiveDateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use superposition_types::{custom_query::{SortBy, deserialize_stringified_list, StringArgs}, Condition, Exp, Overrides};
use superposition_types::{
custom_query::{deserialize_stringified_list, StringArgs},
Condition, Exp, Overrides, SortBy,
};

use crate::db::models::{self, ExperimentStatusType, Variant};

Expand Down Expand Up @@ -168,7 +171,6 @@ pub struct StatusTypes(
pub enum ExperimentSortOn {
LastModifiedAt,
CreatedAt,
ExperimentId,
}

impl Default for ExperimentSortOn {
Expand All @@ -185,6 +187,7 @@ pub struct ExpListFilters {
pub experiment_name: Option<String>,
pub experiment_ids: Option<StringArgs>,
pub created_by: Option<StringArgs>,
pub context: Option<String>,
pub sort_on: Option<ExperimentSortOn>,
pub sort_by: Option<SortBy>,
pub page: Option<i64>,
Expand Down
29 changes: 3 additions & 26 deletions crates/frontend/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use superposition_types::Config;

use crate::{
types::{
ConfigVersionListResponse, DefaultConfig, Dimension, ExpListFilters,
ConfigVersionListResponse, DefaultConfig, Dimension, ExperimentListFilters,
ExperimentResponse, FetchTypeTemplateResponse, FunctionResponse, ListFilters,
PaginatedResponse,
},
Expand Down Expand Up @@ -129,37 +129,14 @@ pub async fn delete_context(
}
}

// #[server(GetExperiments, "/fxn", "GetJson")]
pub async fn fetch_experiments(
exp_filters: ExpListFilters,
pagination_filters: ListFilters,
filters: ExperimentListFilters,
tenant: String,
) -> Result<PaginatedResponse<ExperimentResponse>, ServerFnError> {
let client = reqwest::Client::new();
let host = use_host_server();

let mut query_params = vec![];
if let Some(status) = exp_filters.status {
let status: Vec<String> = status.iter().map(|val| val.to_string()).collect();
query_params.push(format!("status={}", status.join(",")));
}
if let Some(from_date) = exp_filters.from_date {
query_params.push(format!("from_date={}", from_date));
}
if let Some(to_date) = exp_filters.to_date {
query_params.push(format!("to_date={}", to_date));
}
if let Some(page) = pagination_filters.page {
query_params.push(format!("page={}", page));
}
if let Some(count) = pagination_filters.count {
query_params.push(format!("count={}", count));
}
if let Some(all) = pagination_filters.all {
query_params.push(format!("all={}", all));
}

let url = format!("{}/experiments?{}", host, query_params.join("&"));
let url = format!("{}/experiments?{}", host, filters.get_query_params());
let response: PaginatedResponse<ExperimentResponse> = client
.get(url)
.header("x-tenant", tenant)
Expand Down
11 changes: 8 additions & 3 deletions crates/frontend/src/components/experiment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use crate::{
types::{Variant, VariantType},
};

use super::table::types::ColumnSortable;

pub fn gen_variant_table(
variants: &[Variant],
) -> Result<(Vec<Map<String, Value>>, Vec<Column>), String> {
Expand All @@ -20,9 +22,12 @@ pub fn gen_variant_table(
VariantType::CONTROL => format!("{}", variant.variant_type),
VariantType::EXPERIMENTAL => format!("Variant-{}", i),
};
columns.push(Column::new(name.clone(), None, |value: &str, _| {
view! { <span>{value.to_string()}</span> }.into_view()
}));
columns.push(Column::new(
name.clone(),
None,
|value: &str, _| view! { <span>{value.to_string()}</span> }.into_view(),
ColumnSortable::No,
));
for (config, value) in variant.overrides.iter() {
match row_map.get_mut(config) {
Some(c) => {
Expand Down
39 changes: 35 additions & 4 deletions crates/frontend/src/components/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{components::pagination::Pagination, schema::HtmlDisplay};
use self::types::{Column, TablePaginationProps};
use leptos::*;
use serde_json::{json, Map, Value};
use superposition_types::SortBy;

#[component]
pub fn table(
Expand All @@ -29,8 +30,36 @@ pub fn table(
.iter()
.filter(|column| !column.hidden)
.map(|column| {
view! {
<th class="uppercase">{&column.name.replace('_', " ")}</th>
let column_name = (&column.name).replace('_', " ");
match column.sortable.clone() {
types::ColumnSortable::Yes {
sort_fn,
sort_by,
current,
} => {
view! {
<th
class="uppercase"
style="cursor: pointer"
on:click=move |_| sort_fn.call(())
>
{column_name}
{
if current {
match sort_by {
SortBy::Desc => view! { <i class="ri-arrow-down-s-line"></i> },
SortBy::Asc => view! { <i class="ri-arrow-up-s-line"></i> },
}
} else {
view! { <i class="ri-expand-up-down-line"></i> }
}
}
</th>
}
}
types::ColumnSortable::No => {
view! { <th class="uppercase">{column_name}</th> }
}
}
})
.collect_view()}
Expand Down Expand Up @@ -65,8 +94,10 @@ pub fn table(
.filter(|column| !column.hidden)
.map(|column| {
let cname = &column.name;
let value: String =
row.get(cname).unwrap_or(&Value::String(String::new())).html_display();
let value: String = row
.get(cname)
.unwrap_or(&Value::String(String::new()))
.html_display();
view! {
<td class=cell_class
.to_string()>{(column.formatter)(&value, row)}</td>
Expand Down
29 changes: 28 additions & 1 deletion crates/frontend/src/components/table/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::rc::Rc;

use leptos::{view, Callback, IntoView, View};
use serde_json::{Map, Value};
use superposition_types::SortBy;

pub type CellFormatter = Box<Rc<dyn Fn(&str, &Map<String, Value>) -> View>>;

Expand All @@ -10,11 +11,22 @@ pub struct TableSettings {
pub redirect_prefix: Option<String>,
}

#[derive(Clone)]
pub enum ColumnSortable {
Yes {
sort_fn: Callback<()>,
sort_by: SortBy,
current: bool,
},
No,
}

#[derive(Clone)]
pub struct Column {
pub name: String,
pub hidden: bool,
pub formatter: CellFormatter,
pub sortable: ColumnSortable,
}

impl PartialEq for Column {
Expand All @@ -33,16 +45,31 @@ impl Column {
name,
hidden: false,
formatter: Box::new(Rc::new(default_formatter)),
sortable: ColumnSortable::No,
}
}
pub fn default_with_sort(name: String, sortable: ColumnSortable) -> Column {
Column {
name,
hidden: false,
formatter: Box::new(Rc::new(default_formatter)),
sortable,
}
}
pub fn new<NF>(name: String, hidden: Option<bool>, formatter: NF) -> Column
pub fn new<NF>(
name: String,
hidden: Option<bool>,
formatter: NF,
sortable: ColumnSortable,
) -> Column
where
NF: Fn(&str, &Map<String, Value>) -> View + 'static,
{
Column {
name,
hidden: hidden.unwrap_or(false),
formatter: Box::new(Rc::new(formatter)),
sortable,
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion crates/frontend/src/pages/config_version_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use serde_json::{json, Map, Value};

use crate::components::skeleton::Skeleton;
use crate::components::stat::Stat;
use crate::components::table::types::TablePaginationProps;
use crate::components::table::types::{ColumnSortable, TablePaginationProps};
use crate::components::table::{types::Column, Table};
use crate::types::{ConfigVersionListResponse, ListFilters};
use crate::utils::use_url_base;
Expand Down Expand Up @@ -164,6 +164,7 @@ pub fn snapshot_table_columns(tenant: String) -> Vec<Column> {
}
.into_view()
},
ColumnSortable::No,
),
Column::new(
"created_at".to_string(),
Expand All @@ -179,6 +180,7 @@ pub fn snapshot_table_columns(tenant: String) -> Vec<Column> {
};
view! { <span class="w-24">{formatted_date}</span> }.into_view()
},
ColumnSortable::No,
),
Column::new(
"tags".to_string(),
Expand All @@ -198,6 +200,7 @@ pub fn snapshot_table_columns(tenant: String) -> Vec<Column> {
}
.into_view()
},
ColumnSortable::No,
),
]
}
2 changes: 2 additions & 0 deletions crates/frontend/src/pages/custom_types.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::components::table::types::ColumnSortable;
use crate::components::type_template_form::utils::delete_type;
use crate::components::{
drawer::{close_drawer, open_drawer, Drawer, DrawerBtn},
Expand Down Expand Up @@ -95,6 +96,7 @@ pub fn types_page() -> impl IntoView {
}
.into_view()
},
ColumnSortable::No,
),
]
});
Expand Down
10 changes: 8 additions & 2 deletions crates/frontend/src/pages/default_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::components::default_config_form::DefaultConfigForm;
use crate::components::drawer::{close_drawer, open_drawer, Drawer, DrawerBtn};
use crate::components::skeleton::Skeleton;
use crate::components::stat::Stat;
use crate::components::table::types::ColumnSortable;
use crate::components::table::{
types::{Column, TablePaginationProps},
Table,
Expand Down Expand Up @@ -203,13 +204,18 @@ pub fn default_config() -> impl IntoView {
};

vec![
Column::new("key".to_string(), None, expand),
Column::new("key".to_string(), None, expand, ColumnSortable::No),
Column::default("schema".to_string()),
Column::default("value".to_string()),
Column::default("function_name".to_string()),
Column::default("created_at".to_string()),
Column::default("created_by".to_string()),
Column::new("actions".to_string(), None, actions_col_formatter),
Column::new(
"actions".to_string(),
None,
actions_col_formatter,
ColumnSortable::No,
),
]
});

Expand Down
Loading

0 comments on commit 94e1bd4

Please sign in to comment.