add object detail page but inserting relations doesnt work anymore

master
Ondřej Hruška 4 years ago
parent 97af2fd924
commit 61d5791479
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 5
      yopa-web/resources/src/main.js
  2. 2
      yopa-web/resources/static/bundle.js
  3. 2
      yopa-web/resources/static/bundle.js.map
  4. 64
      yopa-web/resources/templates/objects/object_detail.html.tera
  5. 3
      yopa-web/src/main.rs
  6. 115
      yopa-web/src/routes/objects.rs
  7. 28
      yopa/src/lib.rs

@ -37,6 +37,9 @@ window.Yopa = {
onLoad(() => { onLoad(() => {
setTimeout(() => { setTimeout(() => {
document.getElementsByClassName('toast')[0].style.display = 'none'; let toasts = document.getElementsByClassName('toast');
if (toasts.length) {
toasts[0].style.display = 'none';
}
}, 3000) }, 3000)
}) })

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,64 @@
{% extends "_layout" %}
{% block title -%}
{{object.name}}
{%- endblock %}
{% block nav -%}
<a href="/">Home</a>
{%- endblock %}
{% block content -%}
<h1>{{kind}} "{{object.name}}"</h1>
<table>
{% for property in properties %}
{% for value in property.values %}
<tr>
{% if loop.first %}
<th rowspan="{{property.values | length}}">
{{ property.model.name }}
</th>
{% endif %}
<td title="{{value.id}}">
{{ value.value | print_typed_value }}
</td>
</tr>
{% endfor %}
{% endfor %}
</table>
{% for relation in relations %}
<h3>{{relation.model.name}}</h3>
<ul>
{% for instance in relation.instances %}
<li>
<b>{{instance.related.name}}</b>
{% if instance.properties %}
<br>
<small>
{% for property in instance.properties %}
{%- if 0 != loop.index0 -%}
;
{%- endif -%}
{% for value in property.values %}
{%- if loop.first -%}
<i>{{ property.model.name }}:</i>
{%- else -%}
,
{% endif -%}
{{ value.value | print_typed_value }}
{% endfor %}
{% endfor %}
</small>
{% endif %}
</li>
{% endfor %}
</ul>
{% endfor %}
{%- endblock %}

@ -156,7 +156,8 @@ async fn main() -> std::io::Result<()> {
.service(routes::objects::list) .service(routes::objects::list)
.service(routes::objects::create_form) .service(routes::objects::create_form)
.service(routes::objects::create) .service(routes::objects::create)
.service(routes::objects::detail)
//
.service(static_files) .service(static_files)
.default_service(web::to(|| HttpResponse::NotFound().body("File or endpoint not found"))) .default_service(web::to(|| HttpResponse::NotFound().body("File or endpoint not found")))
}) })

@ -12,7 +12,7 @@ use crate::utils::redirect;
use actix_web::web::Json; use actix_web::web::Json;
use serde_json::Value; use serde_json::Value;
use itertools::Itertools; use itertools::Itertools;
use yopa::model::ObjectModel; use yopa::model::{ObjectModel, PropertyModel, RelationModel};
use heck::TitleCase; use heck::TitleCase;
// we only need references here, Context serializes everything to Value. // we only need references here, Context serializes everything to Value.
@ -48,7 +48,7 @@ pub(crate) async fn create_form(
context.insert("model", model); context.insert("model", model);
let relations : Vec<_> = rg.get_relation_models_for_object(model.id).collect(); let relations : Vec<_> = rg.get_relation_models_for_object_model(model.id).collect();
let mut prop_object_ids : Vec<ID> = relations.iter().map(|r| r.id).collect(); let mut prop_object_ids : Vec<ID> = relations.iter().map(|r| r.id).collect();
prop_object_ids.push(model.id); prop_object_ids.push(model.id);
@ -141,3 +141,114 @@ pub(crate) async fn list_inner(session: Session, store: crate::YopaStoreWrapper)
TERA.build_response("objects/index", &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<RelationInstanceView<'a>>
}
#[derive(Debug,Serialize)]
struct RelationInstanceView<'a> {
related: &'a Object,
properties: Vec<PropertyView<'a>>
}
#[get("/object/detail/{id}")]
pub(crate) async fn detail(
id: web::Path<ID>,
store: crate::YopaStoreWrapper
) -> actix_web::Result<impl Responder> {
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)
}

@ -368,7 +368,29 @@ impl Storage {
.into_group_map_by(|model| model.object) .into_group_map_by(|model| model.object)
} }
pub fn get_relation_models_for_object(&self, model_id: ID) -> impl Iterator<Item=&RelationModel> { pub fn get_grouped_prop_models_for_parents<'p, 'a : 'p>(&'a self, parents: &'p [ID]) -> HashMap<ID, Vec<&'p PropertyModel>> {
self.prop_models.values()
.filter(|p| parents.contains(&p.object))
.into_group_map_by(|model| model.object)
}
pub fn get_relations_for_object(&self, object_id: ID) -> impl Iterator<Item=&data::Relation> {
self.relations.values()
.filter(move |rel| rel.object == object_id)
}
pub fn get_values_for_object(&self, object_id: ID) -> impl Iterator<Item=&data::Value> {
self.properties.values()
.filter(move |prop| prop.object == object_id)
}
pub fn get_grouped_values_for_objects<'p, 'a : 'p>(&'a self, parents: &'p [ID]) -> HashMap<ID, Vec<&'p data::Value>> {
self.properties.values()
.filter(move |prop| parents.contains(&prop.object))
.into_group_map_by(|model| model.object)
}
pub fn get_relation_models_for_object_model(&self, model_id: ID) -> impl Iterator<Item=&RelationModel> {
self.rel_models.values() self.rel_models.values()
.filter(move |model| model.object == model_id) .filter(move |model| model.object == model_id)
} }
@ -398,6 +420,10 @@ impl Storage {
.into_group_map_by(|model| model.related) .into_group_map_by(|model| model.related)
} }
pub fn get_object(&self, id : ID) -> Option<&Object> {
self.objects.get(&id)
}
// Updates // Updates
pub fn update_object(&mut self, model : ObjectModel) -> Result<(), StorageError> { pub fn update_object(&mut self, model : ObjectModel) -> Result<(), StorageError> {
if model.name.is_empty() { if model.name.is_empty() {

Loading…
Cancel
Save