airlab-web 0.1.3

airlab backend
use crate::web::Result;
use crate::web::mw_auth::CtxW;
use crate::web_util::get_member_id;
use airlab_lib::model::ModelManager;
use airlab_lib::model::panel::{
    Panel, PanelBmc, PanelForCreate, PanelForUpdate, PanelPayloadForUpdate,
};
use airlab_lib::model::panel_element::{
    PanelElement, PanelElementBmc, PanelElementFilter, PanelElementForCreate, PanelElementForUpdate,
};
use airlab_lib::model::view_panel::{ViewPanel, ViewPanelBmc};
use airlab_lib::model::view_panel_element::{ViewPanelElement, ViewPanelElementBmc};
use axum::extract::{Json as eJson, Path, State};
use axum::routing::{get, patch, post, put};
use axum::{Json, Router};
use modql::filter::ListOptions;
use serde::Deserialize;
use serde_json::{Value, json};
use std::collections::{HashMap, HashSet};
#[allow(unused_imports)]
use tracing::{debug, warn};

pub fn routes(mm: ModelManager) -> Router {
    Router::new()
        .route("/api/v1/panels", post(api_post_panel_handler))
        .route(
            "/api/v1/panels/:panel_id/elements",
            get(api_panel_elements_handler),
        )
        .route("/api/v1/panels/:panel_id", patch(api_patch_panel_handler))
        .route("/api/v1/panels/:panel_id", get(api_panel_handler))
        .route("/api/v1/panels/:panel_id", put(api_put_panel_handler))
        .route(
            "/api/v1/panels/:panel_id/archive",
            patch(api_patch_archive_panel_handler),
        )
        .route("/api/v1/panels", get(api_panels_handler))
        .with_state(mm)
}

async fn api_panel_elements_handler(
    State(mm): State<ModelManager>,
    ctx: CtxW,
    Path(panel_id): Path<i32>,
) -> Result<Json<Value>> {
    debug!("HANDLER - api_panel_elements_handler");
    let ctx = ctx.0;
    let filters: Vec<PanelElementFilter> = serde_json::from_value(json!([
        {
            "panel_id": panel_id
        }
    ]))?;

    let panel_elements: Vec<ViewPanelElement> =
        ViewPanelElementBmc::list(&ctx, &mm, Some(filters), None).await?;
    Ok(Json(json!(panel_elements)))
}

#[derive(Deserialize, Debug)]
struct ElementPayload {
    #[serde(rename = "conjugateId")]
    conjugate_id: i32,
    #[serde(rename = "dilutionType")]
    dilution_type: i32,
}

#[derive(Deserialize, Debug)]
struct PanelForCreatePayload {
    name: String,
    #[serde(rename = "groupId")]
    group_id: i32,
    description: Option<String>,
    #[serde(rename = "isFluorophore")]
    is_fluorophore: bool,
    #[serde(rename = "isLocked")]
    is_locked: bool,
    application: Option<i32>,
    elements: Vec<ElementPayload>,
}

async fn api_post_panel_handler(
    State(mm): State<ModelManager>,
    ctx: CtxW,
    eJson(payload): eJson<PanelForCreatePayload>,
) -> Result<Json<Value>> {
    debug!("HANDLER - api_post_panel_handler: {:?}", payload);
    let ctx = ctx.0;
    let created_by = get_member_id(&ctx, &mm, payload.group_id, ctx.user_id()).await?;
    let mut forc = PanelForCreate {
        created_by: Some(created_by),
        name: Some(payload.name),
        application: payload.application,
        description: payload.description,
        group_id: payload.group_id,
        is_fluorophore: payload.is_fluorophore,
        is_locked: payload.is_locked,
    };
    forc.created_by = Some(created_by);
    let panel_id = PanelBmc::create(&ctx, &mm, forc).await?;

    for element in payload.elements {
        let element_c = PanelElementForCreate {
            concentration: None,
            conjugate_id: element.conjugate_id,
            dilution_type: element.dilution_type,
            panel_id,
        };
        PanelElementBmc::create(&ctx, &mm, element_c).await?;
    }

    let panel: Panel = PanelBmc::get(&ctx, &mm, panel_id).await?;
    Ok(Json(json!(panel)))
}

#[derive(Deserialize, Debug)]
struct PanelArchive {
    state: bool,
}

async fn api_patch_archive_panel_handler(
    State(mm): State<ModelManager>,
    ctx: CtxW,
    Path(panel_id): Path<i32>,
    eJson(payload): eJson<PanelArchive>,
) -> Result<Json<Value>> {
    debug!(
        "HANDLER - api_patch_archive_panel: {}; {:?} {}",
        panel_id, payload, payload.state
    );
    let ctx = ctx.0;
    let panel_update = PanelForUpdate {
        is_archived: Some(true),
        ..Default::default()
    };

    PanelBmc::update(&ctx, &mm, panel_id, panel_update).await?;
    let panel: Panel = PanelBmc::get(&ctx, &mm, panel_id).await?;
    Ok(Json(json!(panel)))
}

#[derive(Deserialize, Debug)]
struct PanelDuplicate {
    #[serde(rename = "groupId")]
    group_id: i32,
    name: String,
}

async fn api_put_panel_handler(
    State(mm): State<ModelManager>,
    ctx: CtxW,
    Path(panel_id): Path<i32>,
    eJson(payload): eJson<PanelDuplicate>,
) -> Result<Json<Value>> {
    debug!(
        "HANDLER - api_panel: {}; {:?} {} {}",
        panel_id, payload, payload.group_id, payload.name
    );
    let ctx = ctx.0;
    let panel = PanelBmc::get(&ctx, &mm, panel_id).await?;
    let created_by = get_member_id(&ctx, &mm, panel.group_id, ctx.user_id()).await?;
    let panel_c = PanelForCreate {
        name: Some(payload.name),
        description: Some(format!(
            "Duplicate of {} (id: {})",
            panel.name.unwrap_or(String::new()),
            panel.id
        )),
        created_by: Some(created_by),
        group_id: payload.group_id,
        application: panel.application,
        is_fluorophore: panel.is_fluorophore,
        is_locked: false,
    };
    let new_panel_id = PanelBmc::create(&ctx, &mm, panel_c).await?;
    let filters: Vec<PanelElementFilter> = serde_json::from_value(json!([
        {
            "panel_id": panel_id
        }
    ]))?;

    let panel_elements: Vec<PanelElement> =
        PanelElementBmc::list(&ctx, &mm, Some(filters), None).await?;
    for element in panel_elements {
        let element_c = PanelElementForCreate {
            concentration: element.concentration,
            conjugate_id: element.conjugate_id,
            dilution_type: i32::from(element.dilution_type),
            panel_id: new_panel_id,
        };
        PanelElementBmc::create(&ctx, &mm, element_c).await?;
    }

    let panel: Panel = PanelBmc::get(&ctx, &mm, new_panel_id).await?;
    Ok(Json(json!(panel)))
}

async fn api_patch_panel_handler(
    State(mm): State<ModelManager>,
    ctx: CtxW,
    Path(panel_id): Path<i32>,
    eJson(payload): eJson<PanelPayloadForUpdate>,
) -> Result<Json<Value>> {
    debug!(
        "HANDLER - api_patch_panel_handler: {}; {:?}",
        panel_id, payload
    );
    let ctx = ctx.0;
    let filters: Vec<PanelElementFilter> =
        serde_json::from_value(json!([{"panel_id": {"$eq": panel_id}}]))?;
    let op = ListOptions {
        limit: Some(10_000),
        ..Default::default()
    };
    let current_elements = PanelElementBmc::list(&ctx, &mm, Some(filters), Some(op)).await?;
    let mut present_ids: HashSet<i32> = current_elements.iter().map(|e| e.id).collect();
    let conjugates_present: HashMap<i32, i32> = current_elements
        .into_iter()
        .map(|e| (e.conjugate_id, e.id))
        .collect();
    for element in &payload.elements {
        if let Some(id) = conjugates_present.get(&element.conjugate_id) {
            present_ids.remove(&id);
            let element_u = PanelElementForUpdate {
                concentration: element.concentration,
                dilution_type: element.dilution_type,
            };
            PanelElementBmc::update(&ctx, &mm, *id, element_u).await?;
        } else {
            let for_create = PanelElementForCreate {
                panel_id,
                concentration: element.concentration,
                dilution_type: element.dilution_type,
                conjugate_id: element.conjugate_id,
            };
            PanelElementBmc::create(&ctx, &mm, for_create).await?;
        }
    }
    for id_to_remove in present_ids {
        println!("Warning: removing panel_element id: {id_to_remove}");
        warn!("Warning: removing panel_element id: {id_to_remove}");
        PanelElementBmc::delete(&ctx, &mm, id_to_remove).await?;
    }
    let pu = PanelForUpdate {
        name: payload.name.clone(),
        is_locked: payload.is_locked,
        is_fluorophore: payload.is_fluorophore,
        description: payload.description.clone(),
        application: payload.application,
        ..Default::default()
    };

    if pu.name.is_none()
        && pu.is_locked.is_none()
        && pu.is_fluorophore.is_none()
        && pu.description.is_none()
        && pu.application.is_none()
    {
    } else {
        PanelBmc::update(&ctx, &mm, panel_id, pu).await?;
    }

    let panel: Panel = PanelBmc::get(&ctx, &mm, panel_id).await?;
    Ok(Json(json!(panel)))
}

async fn api_panel_handler(
    State(mm): State<ModelManager>,
    ctx: CtxW,
    Path(panel_id): Path<i32>,
) -> Result<Json<Value>> {
    debug!("HANDLER - api_panel_handler: {}", panel_id);
    let ctx = ctx.0;

    let panel: ViewPanel = ViewPanelBmc::get(&ctx, &mm, panel_id).await?;
    Ok(Json(json!(panel)))
}

async fn api_panels_handler(State(mm): State<ModelManager>, ctx: CtxW) -> Result<Json<Value>> {
    debug!("HANDLER - api_panels_handler");
    let ctx = ctx.0;

    let panels: Vec<Panel> = PanelBmc::list(&ctx, &mm, None, None).await?;
    Ok(Json(json!(panels)))
}