list of objects and some other stuff
This commit is contained in:
+2
-1
@@ -11,7 +11,7 @@ build = "build.rs"
|
||||
yopa = { path = "../yopa", features = [] }
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
|
||||
heck = "0.3.2"
|
||||
log = "0.4.14"
|
||||
simple-logging = "2.0.2"
|
||||
actix-web = "3"
|
||||
@@ -22,6 +22,7 @@ tera = "1.6.1"
|
||||
actix-web-static-files = "3.0"
|
||||
once_cell = "1.5.2"
|
||||
rand = "0.8.3"
|
||||
itertools = "0.10.0"
|
||||
|
||||
tokio = { version="0.2.6", features=["full"] }
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
assets:
|
||||
cd resources && npm run build
|
||||
@@ -90,10 +90,13 @@ export default {
|
||||
data: data
|
||||
})
|
||||
.then(function (response) {
|
||||
console.log('Response', response);
|
||||
location.href = '/objects';
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log('Error', error);
|
||||
// TODO show error toast instead
|
||||
alert(error.response ?
|
||||
error.response.data :
|
||||
error)
|
||||
});
|
||||
},
|
||||
|
||||
@@ -129,13 +132,15 @@ export default {
|
||||
<property v-for="(property, pi) in properties" :model="property" :values="values[property.id]" :key="pi"></property>
|
||||
</table>
|
||||
|
||||
<h3>Relations</h3>
|
||||
<div v-if="relations.length > 0">
|
||||
<h3>Relations</h3>
|
||||
|
||||
<new-relation
|
||||
v-for="relation in relations"
|
||||
:ref="setRelationRef"
|
||||
:model_id="relation.id"
|
||||
:objects="objects"
|
||||
:schema="schema"
|
||||
></new-relation>
|
||||
<new-relation
|
||||
v-for="relation in relations"
|
||||
:ref="setRelationRef"
|
||||
:model_id="relation.id"
|
||||
:objects="objects"
|
||||
:schema="schema"
|
||||
></new-relation>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -34,3 +34,9 @@ window.Yopa = {
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
onLoad(() => {
|
||||
setTimeout(() => {
|
||||
document.getElementsByClassName('toast')[0].style.display = 'none';
|
||||
}, 3000)
|
||||
})
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
-2
@@ -6,12 +6,12 @@
|
||||
{%- endblock %}
|
||||
|
||||
{% block nav -%}
|
||||
<a href="/takeout">Takeout</a>
|
||||
<a href="/">Home</a>
|
||||
{%- endblock %}
|
||||
|
||||
{% block content -%}
|
||||
|
||||
<h1>Welcome to YOPA</h1>
|
||||
<h1>Models</h1>
|
||||
|
||||
<a href="/model/object/create">New model</a>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{% extends "_layout" %}
|
||||
|
||||
{% block title -%}
|
||||
Objects
|
||||
{%- endblock %}
|
||||
|
||||
{% block nav -%}
|
||||
<a href="/models">Edit models</a>
|
||||
<a href="/takeout">JSON</a>
|
||||
{%- endblock %}
|
||||
|
||||
{% block content -%}
|
||||
|
||||
<h1>Objects</h1>
|
||||
|
||||
{% for table in models %}
|
||||
|
||||
<h2>{{ table.model.name }}</h2>
|
||||
<a href="/object/create/{{table.model.id}}">Add {{ table.model.name }}</a>
|
||||
|
||||
<ul>
|
||||
{% for object in table.objects %}
|
||||
<li><a href="/object/detail/{{object.id}}">{{object.name}}</a>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{%- endblock %}
|
||||
+21
-18
@@ -133,26 +133,29 @@ async fn main() -> std::io::Result<()> {
|
||||
.service(routes::index)
|
||||
.service(routes::takeout)
|
||||
//
|
||||
.service(routes::object_model::create_form)
|
||||
.service(routes::object_model::create)
|
||||
.service(routes::object_model::update_form)
|
||||
.service(routes::object_model::update)
|
||||
.service(routes::object_model::delete)
|
||||
.service(routes::models::list)
|
||||
//
|
||||
.service(routes::relation_model::create_form)
|
||||
.service(routes::relation_model::create)
|
||||
.service(routes::relation_model::update_form)
|
||||
.service(routes::relation_model::update)
|
||||
.service(routes::relation_model::delete)
|
||||
.service(routes::models::object::create_form)
|
||||
.service(routes::models::object::create)
|
||||
.service(routes::models::object::update_form)
|
||||
.service(routes::models::object::update)
|
||||
.service(routes::models::object::delete)
|
||||
//
|
||||
.service(routes::property_model::create_form)
|
||||
.service(routes::property_model::create)
|
||||
.service(routes::property_model::update_form)
|
||||
.service(routes::property_model::update)
|
||||
.service(routes::property_model::delete)
|
||||
|
||||
.service(routes::object::create_form)
|
||||
.service(routes::object::create)
|
||||
.service(routes::models::relation::create_form)
|
||||
.service(routes::models::relation::create)
|
||||
.service(routes::models::relation::update_form)
|
||||
.service(routes::models::relation::update)
|
||||
.service(routes::models::relation::delete)
|
||||
//
|
||||
.service(routes::models::property::create_form)
|
||||
.service(routes::models::property::create)
|
||||
.service(routes::models::property::update_form)
|
||||
.service(routes::models::property::update)
|
||||
.service(routes::models::property::delete)
|
||||
//
|
||||
.service(routes::objects::list)
|
||||
.service(routes::objects::create_form)
|
||||
.service(routes::objects::create)
|
||||
|
||||
.service(static_files)
|
||||
.default_service(web::to(|| HttpResponse::NotFound().body("File or endpoint not found")))
|
||||
|
||||
+7
-66
@@ -13,75 +13,16 @@ use yopa::model::{ObjectModel, PropertyModel, RelationModel};
|
||||
use crate::session_ext::SessionExt;
|
||||
use crate::TERA;
|
||||
use crate::tera_ext::TeraExt;
|
||||
use crate::routes::relation_model::RelationModelDisplay;
|
||||
use crate::routes::object_model::ObjectModelDisplay;
|
||||
use crate::routes::models::relation::RelationModelDisplay;
|
||||
use crate::routes::models::object::ObjectModelDisplay;
|
||||
|
||||
pub(crate) mod object_model;
|
||||
pub(crate) mod relation_model;
|
||||
pub(crate) mod property_model;
|
||||
|
||||
pub(crate) mod object;
|
||||
pub(crate) mod models;
|
||||
pub(crate) mod objects;
|
||||
|
||||
#[get("/")]
|
||||
pub(crate) async fn index(session: Session, store: crate::YopaStoreWrapper) -> actix_web::Result<impl Responder> {
|
||||
let rg = store.read().await;
|
||||
|
||||
let models_iter = rg.get_object_models();
|
||||
|
||||
// object and relation props
|
||||
let mut model_props = rg.get_grouped_prop_models();
|
||||
|
||||
let mut model_relations = rg.get_grouped_relation_models();
|
||||
let mut model_rec_relations = rg.get_grouped_reciprocal_relation_models();
|
||||
|
||||
let mut models = vec![];
|
||||
for om in models_iter {
|
||||
let mut relations = model_relations.remove(&om.id).unwrap_or_default();
|
||||
let mut relations = relations.into_iter().map(|rm| {
|
||||
let mut rprops = model_props.get(&rm.id).cloned().unwrap_or_default();
|
||||
rprops.sort_by_key(|m| &m.name);
|
||||
|
||||
RelationModelDisplay {
|
||||
model: rm,
|
||||
related_name: rg.get_model_name(rm.related),
|
||||
properties: rprops,
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
relations.sort_by_key(|d| &d.model.name);
|
||||
|
||||
// Relations coming INTO this model
|
||||
let mut reciprocal_relations = model_rec_relations.remove(&om.id).unwrap_or_default();
|
||||
let mut reciprocal_relations = reciprocal_relations.into_iter().map(|rm| {
|
||||
let mut rprops = model_props.get(&rm.id).cloned().unwrap_or_default();
|
||||
rprops.sort_by_key(|m| &m.name);
|
||||
|
||||
RelationModelDisplay {
|
||||
model: rm,
|
||||
related_name: rg.get_model_name(rm.object),
|
||||
properties: rprops,
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
reciprocal_relations.sort_by_key(|d| &d.model.reciprocal_name);
|
||||
|
||||
let mut properties = model_props.remove(&om.id).unwrap_or_default();
|
||||
properties.sort_by_key(|m| &m.name);
|
||||
|
||||
models.push(ObjectModelDisplay {
|
||||
id: om.id,
|
||||
name: &om.name,
|
||||
properties,
|
||||
relations,
|
||||
reciprocal_relations,
|
||||
})
|
||||
}
|
||||
|
||||
models.sort_by_key(|m| m.name);
|
||||
|
||||
let mut ctx = tera::Context::new();
|
||||
ctx.insert("models", &models);
|
||||
session.render_flash(&mut ctx);
|
||||
|
||||
TERA.build_response("models/schema", &ctx)
|
||||
pub(crate) async fn index(session: Session, store: crate::YopaStoreWrapper) -> actix_web::Result<HttpResponse> {
|
||||
objects::list_inner(session, store)
|
||||
.await
|
||||
}
|
||||
|
||||
#[get("/takeout")]
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
use actix_session::Session;
|
||||
use actix_web::Responder;
|
||||
use crate::routes::models::relation::RelationModelDisplay;
|
||||
use crate::routes::models::object::ObjectModelDisplay;
|
||||
use crate::session_ext::SessionExt;
|
||||
use crate::TERA;
|
||||
use crate::tera_ext::TeraExt;
|
||||
|
||||
pub(crate) mod object;
|
||||
pub(crate) mod relation;
|
||||
pub(crate) mod property;
|
||||
|
||||
#[get("/models")]
|
||||
pub(crate) async fn list(session: Session, store: crate::YopaStoreWrapper) -> actix_web::Result<impl Responder> {
|
||||
let rg = store.read().await;
|
||||
|
||||
let models_iter = rg.get_object_models();
|
||||
|
||||
// object and relation props
|
||||
let mut model_props = rg.get_grouped_prop_models();
|
||||
|
||||
let mut model_relations = rg.get_grouped_relation_models();
|
||||
let mut model_rec_relations = rg.get_grouped_reciprocal_relation_models();
|
||||
|
||||
let mut models = vec![];
|
||||
for om in models_iter {
|
||||
let mut relations = model_relations.remove(&om.id).unwrap_or_default();
|
||||
let mut relations = relations.into_iter().map(|rm| {
|
||||
let mut rprops = model_props.get(&rm.id).cloned().unwrap_or_default();
|
||||
rprops.sort_by_key(|m| &m.name);
|
||||
|
||||
RelationModelDisplay {
|
||||
model: rm,
|
||||
related_name: rg.get_model_name(rm.related),
|
||||
properties: rprops,
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
relations.sort_by_key(|d| &d.model.name);
|
||||
|
||||
// Relations coming INTO this model
|
||||
let mut reciprocal_relations = model_rec_relations.remove(&om.id).unwrap_or_default();
|
||||
let mut reciprocal_relations = reciprocal_relations.into_iter().map(|rm| {
|
||||
let mut rprops = model_props.get(&rm.id).cloned().unwrap_or_default();
|
||||
rprops.sort_by_key(|m| &m.name);
|
||||
|
||||
RelationModelDisplay {
|
||||
model: rm,
|
||||
related_name: rg.get_model_name(rm.object),
|
||||
properties: rprops,
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
reciprocal_relations.sort_by_key(|d| &d.model.reciprocal_name);
|
||||
|
||||
let mut properties = model_props.remove(&om.id).unwrap_or_default();
|
||||
properties.sort_by_key(|m| &m.name);
|
||||
|
||||
models.push(ObjectModelDisplay {
|
||||
id: om.id,
|
||||
name: &om.name,
|
||||
properties,
|
||||
relations,
|
||||
reciprocal_relations,
|
||||
})
|
||||
}
|
||||
|
||||
models.sort_by_key(|m| m.name);
|
||||
|
||||
let mut ctx = tera::Context::new();
|
||||
ctx.insert("models", &models);
|
||||
session.render_flash(&mut ctx);
|
||||
|
||||
TERA.build_response("models/index", &ctx)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||
use yopa::ID;
|
||||
use yopa::model::{ObjectModel, PropertyModel};
|
||||
|
||||
use crate::routes::relation_model::RelationModelDisplay;
|
||||
use crate::routes::models::relation::RelationModelDisplay;
|
||||
use crate::session_ext::SessionExt;
|
||||
use crate::TERA;
|
||||
use crate::tera_ext::TeraExt;
|
||||
@@ -55,7 +55,7 @@ pub(crate) async fn create(
|
||||
Ok(_id) => {
|
||||
debug!("Object created, redirecting to root");
|
||||
session.flash_success(format!("Object model \"{}\" created.", form.name));
|
||||
redirect("/")
|
||||
redirect("/models")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error creating model: {}", e);
|
||||
@@ -109,7 +109,7 @@ pub(crate) async fn update(
|
||||
Ok(_id) => {
|
||||
debug!("Object updated, redirecting to root");
|
||||
session.flash_success(format!("Object model \"{}\" updated.", form.name));
|
||||
redirect("/")
|
||||
redirect("/models")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error updating model: {}", e);
|
||||
@@ -132,7 +132,7 @@ pub(crate) async fn delete(
|
||||
Ok(om) => {
|
||||
debug!("Object model deleted, redirecting to root");
|
||||
session.flash_success(format!("Object model \"{}\" deleted.", om.name));
|
||||
redirect("/")
|
||||
redirect("/models")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error deleting object model: {}", e);
|
||||
@@ -9,7 +9,7 @@ use crate::session_ext::SessionExt;
|
||||
use crate::TERA;
|
||||
use crate::tera_ext::TeraExt;
|
||||
use crate::utils::{ParseOrBadReq, redirect};
|
||||
use crate::routes::relation_model::ObjectOrRelationModelDisplay;
|
||||
use crate::routes::models::relation::ObjectOrRelationModelDisplay;
|
||||
|
||||
#[get("/model/property/create/{object_id}")]
|
||||
pub(crate) async fn create_form(
|
||||
@@ -148,7 +148,7 @@ pub(crate) async fn create(
|
||||
Ok(_id) => {
|
||||
debug!("Property created, redirecting to root");
|
||||
session.flash_success(format!("Property model \"{}\" created.", form.name));
|
||||
redirect("/")
|
||||
redirect("/models")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error creating property model: {}", e);
|
||||
@@ -170,12 +170,12 @@ pub(crate) async fn delete(
|
||||
Ok(rm) => {
|
||||
debug!("Property deleted, redirecting to root");
|
||||
session.flash_success(format!("Property \"{}\" deleted.", rm.name));
|
||||
redirect("/")
|
||||
redirect("/models")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error deleting property: {}", e);
|
||||
session.flash_error(e.to_string());
|
||||
redirect("/") // back?
|
||||
redirect("/models") // back?
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -256,7 +256,7 @@ pub(crate) async fn update(
|
||||
Ok(_id) => {
|
||||
debug!("Relation updated, redirecting to root");
|
||||
session.flash_success(format!("Property \"{}\" updated.", form.name));
|
||||
redirect("/")
|
||||
redirect("/models")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error updating model: {}", e);
|
||||
@@ -53,8 +53,6 @@ pub(crate) async fn create_form(
|
||||
context.insert("models", &models);
|
||||
context.insert("object", &object);
|
||||
|
||||
|
||||
|
||||
TERA.build_response("models/relation_create", &context)
|
||||
}
|
||||
|
||||
@@ -90,7 +88,7 @@ pub(crate) async fn create(
|
||||
Ok(_id) => {
|
||||
debug!("Relation created, redirecting to root");
|
||||
session.flash_success(format!("Relation model \"{}\" created.", form.name));
|
||||
redirect("/")
|
||||
redirect("/models")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error creating relation model: {}", e);
|
||||
@@ -118,12 +116,12 @@ pub(crate) async fn delete(
|
||||
Ok(rm) => {
|
||||
debug!("Relation deleted, redirecting to root");
|
||||
session.flash_success(format!("Relation model \"{}\" deleted.", rm.name));
|
||||
redirect("/")
|
||||
redirect("/models")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error deleting relation model: {}", e);
|
||||
session.flash_error(e.to_string());
|
||||
redirect("/") // back?
|
||||
redirect("/models") // back?
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,7 +186,7 @@ pub(crate) async fn update(
|
||||
Ok(_id) => {
|
||||
debug!("Relation updated, redirecting to root");
|
||||
session.flash_success(format!("Relation model \"{}\" updated.", form.name));
|
||||
redirect("/")
|
||||
redirect("/models")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error updating model: {}", e);
|
||||
@@ -1,7 +1,7 @@
|
||||
use actix_session::Session;
|
||||
use actix_web::{Responder, web, HttpResponse};
|
||||
use crate::session_ext::SessionExt;
|
||||
use crate::routes::object_model::ObjectModelForm;
|
||||
use crate::routes::models::object::ObjectModelForm;
|
||||
use crate::TERA;
|
||||
use crate::tera_ext::TeraExt;
|
||||
use yopa::{ID, model};
|
||||
@@ -11,6 +11,9 @@ use yopa::insert::InsertObj;
|
||||
use crate::utils::redirect;
|
||||
use actix_web::web::Json;
|
||||
use serde_json::Value;
|
||||
use itertools::Itertools;
|
||||
use yopa::model::ObjectModel;
|
||||
use heck::TitleCase;
|
||||
|
||||
// we only need references here, Context serializes everything to Value.
|
||||
// cloning would be a waste of cycles
|
||||
@@ -85,13 +88,17 @@ pub(crate) async fn create(
|
||||
//
|
||||
// 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!("Object \"{}\" created.", name));
|
||||
session.flash_success(format!("{} \"{}\" created.", model_name, name));
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -100,3 +107,37 @@ pub(crate) async fn create(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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<impl Responder> {
|
||||
list_inner(session, store).await
|
||||
}
|
||||
|
||||
pub(crate) async fn list_inner(session: Session, store: crate::YopaStoreWrapper) -> actix_web::Result<HttpResponse> {
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user