use actix_session::Session; use actix_web::{Responder, web, HttpResponse}; use crate::session_ext::SessionExt; use crate::routes::models::object::ObjectModelForm; use crate::TERA; use crate::tera_ext::TeraExt; use yopa::{ID, model}; use yopa::data::Object; use serde::{Serialize,Deserialize}; use yopa::insert::InsertObj; use crate::utils::redirect; use actix_web::web::Json; use serde_json::Value; use itertools::Itertools; use yopa::model::{ObjectModel, PropertyModel, RelationModel}; use heck::TitleCase; // we only need references here, Context serializes everything to Value. // cloning would be a waste of cycles #[derive(Debug, Default, Serialize, Clone)] pub struct Schema<'a> { pub obj_models: Vec<&'a model::ObjectModel>, pub rel_models: Vec<&'a model::RelationModel>, pub prop_models: Vec<&'a model::PropertyModel>, } #[derive(Serialize,Debug,Clone)] pub struct ObjectCreateData<'a> { pub model_id: ID, pub schema: Schema<'a>, pub objects: Vec<&'a Object>, } #[get("/object/create/{model_id}")] pub(crate) async fn create_form( model_id: web::Path, store: crate::YopaStoreWrapper, session: Session ) -> actix_web::Result { let mut context = tera::Context::new(); session.render_flash(&mut context); let rg = store.read().await; let model = rg.get_object_model(*model_id) .ok_or_else(|| actix_web::error::ErrorNotFound("No such model"))?; context.insert("model", model); let relations : Vec<_> = rg.get_relation_models_for_object_model(model.id).collect(); let mut prop_object_ids : Vec = relations.iter().map(|r| r.id).collect(); prop_object_ids.push(model.id); prop_object_ids.sort(); prop_object_ids.dedup(); let mut related_ids : Vec<_> = relations.iter().map(|r| r.related).collect(); related_ids.sort(); related_ids.dedup(); let form_data = ObjectCreateData { model_id: model.id, schema: Schema { obj_models: rg.get_object_models().collect(), // TODO get only the ones that matter here rel_models: relations, prop_models: rg.get_property_models_for_parents(&prop_object_ids).collect() }, objects: rg.get_objects_of_type(&related_ids).collect() }; context.insert("form_data", &form_data); TERA.build_response("objects/object_create", &context) } #[post("/object/create")] pub(crate) async fn create( form: web::Json, store: crate::YopaStoreWrapper, session: Session, ) -> actix_web::Result { warn!("{:?}", form); // let des : InsertObj = serde_json::from_value(form.into_inner()).unwrap(); // // Ok(HttpResponse::Ok().finish()) // let mut wg = store.write().await; let form = form.into_inner(); let name = form.name.clone(); let model_name = wg.get_model_name(form.model_id).to_owned().to_title_case(); match wg.insert_object(form) { Ok(_id) => { debug!("Object created, redirecting to root"); session.flash_success(format!("{} \"{}\" created.", model_name, name)); Ok(HttpResponse::Ok().finish()) } Err(e) => { warn!("Error creating model: {}", e); Ok(HttpResponse::BadRequest().body(e.to_string())) } } } #[derive(Debug, Serialize, Clone)] struct ModelWithObjects<'a> { model : &'a ObjectModel, objects: Vec<&'a Object> } #[get("/objects")] pub(crate) async fn list(session: Session, store: crate::YopaStoreWrapper) -> actix_web::Result { list_inner(session, store).await } pub(crate) async fn list_inner(session: Session, store: crate::YopaStoreWrapper) -> actix_web::Result { let rg = store.read().await; let mut objects_by_model = rg.get_grouped_objects(); let mut models : Vec<_> = rg.get_object_models() .sorted_by_key(|m| &m.name) .map(|model| { let mut objects = objects_by_model.remove(&model.id).unwrap_or_default(); objects.sort_by_key(|o| &o.name); ModelWithObjects { model, objects } }).collect(); let mut ctx = tera::Context::new(); ctx.insert("models", &models); session.render_flash(&mut ctx); TERA.build_response("objects/index", &ctx) } #[derive(Debug,Serialize)] struct PropertyView<'a> { model : &'a PropertyModel, values: Vec<&'a yopa::data::Value> } #[derive(Debug,Serialize)] struct RelationView<'a> { model : &'a RelationModel, related_name: &'a str, instances: Vec> } #[derive(Debug,Serialize)] struct RelationInstanceView<'a> { related: &'a Object, properties: Vec> } #[get("/object/detail/{id}")] pub(crate) async fn detail( id: web::Path, store: crate::YopaStoreWrapper ) -> actix_web::Result { let object_id = *id; let mut context = tera::Context::new(); let rg = store.read().await; let object = rg.get_object(object_id) .ok_or_else(|| actix_web::error::ErrorNotFound("No such object"))?; let model = rg.get_object_model(object.model) .ok_or_else(|| actix_web::error::ErrorNotFound("No such model"))?; context.insert("object", object); context.insert("model", model); context.insert("kind", &rg.get_model_name(object.model)); let relations = rg.get_relations_for_object(object_id).collect_vec(); // values by parent ID let mut ids_to_get_values_for = relations.iter().map(|r| r.id).collect_vec(); ids_to_get_values_for.push(object_id); let mut grouped_values = { rg.get_grouped_values_for_objects(&ids_to_get_values_for) }; // object's own properties { let mut object_values_by_model = grouped_values .remove(&object_id).unwrap_or_default().into_iter() .into_group_map_by(|value| value.model); let mut view_object_properties = vec![]; for (prop_model_id, values) in object_values_by_model { view_object_properties.push(PropertyView { model: rg.get_property_model(prop_model_id).unwrap(), values }) } context.insert("properties", &view_object_properties); } // go through relations { let grouped_relations = relations.iter() .into_group_map_by(|relation| relation.model); let mut relation_views = vec![]; for (model_id, relations) in grouped_relations { let mut instances = vec![]; for rel in relations { let related_obj = match rg.get_object(rel.related) { None => continue, Some(obj) => obj }; let mut rel_values_by_model = grouped_values .remove(&rel.id).unwrap_or_default().into_iter() .into_group_map_by(|value| value.model); let mut view_rel_properties = vec![]; for (prop_model_id, values) in rel_values_by_model { view_rel_properties.push(PropertyView { model: rg.get_property_model(prop_model_id).unwrap(), values }) } instances.push(RelationInstanceView { related: related_obj, properties: view_rel_properties }) } relation_views.push(RelationView { model: rg.get_relation_model(model_id).unwrap(), related_name: rg.get_model_name(model_id), instances }) } context.insert("relations", &relation_views); } TERA.build_response("objects/object_detail", &context) }