Change ModelForest inner structure

This commit is contained in:
Ludovic Stephan 2017-04-05 22:10:21 -03:00
parent e4dd434608
commit df47bedae1
3 changed files with 80 additions and 105 deletions

View file

@ -450,22 +450,28 @@ class ArticleCategory extends ModelObject {
/** /**
* Properties associated to a category * Properties associated to a category
* @default <tt>['id', 'name']</tt> * @default <tt>['id', 'name', 'has_addcost', 'article']</tt>
* @see {@link Models.ModelObject.props|ModelObject.props} * @see {@link Models.ModelObject.props|ModelObject.props}
*/ */
static get props() { static get props() {
return ['id', 'name', 'has_addcost']; return ['id', 'name', 'has_addcost', 'articles'];
} }
/** /**
* Default values for ArticleCategory model instances. * Default values for ArticleCategory model instances.
* @default <tt>{ 'id': 0, 'name': '' }</tt> * @default <tt>{ 'id': 0, 'name': '', 'has_addcost': true, 'articles': [] }</tt>
* @see {@link Models.ModelObject.default_data|ModelObject.default_data} * @see {@link Models.ModelObject.default_data|ModelObject.default_data}
*/ */
static get default_data() { static get default_data() {
return {'id': 0, 'name': '', 'has_addcost': true}; return {'id': 0, 'name': '', 'has_addcost': true, 'articles': []};
} }
/**
* Verbose name for ArticleCategory model
* @default <tt>'category'</tt>
*/
static get verbose_name() { return 'category'; }
/** /**
* @default {@link Formatters.ArticleCategoryFormatter} * @default {@link Formatters.ArticleCategoryFormatter}
*/ */
@ -490,7 +496,7 @@ class ArticleCategory extends ModelObject {
class Article extends ModelObject { class Article extends ModelObject {
/** /**
* Properties associated to an article * Properties associated to an article
* @default <tt>['id', 'name']</tt> * @default <tt>['id', 'name', 'price', 'stock', 'category']</tt>
* @see {@link Models.ModelObject.props|ModelObject.props} * @see {@link Models.ModelObject.props|ModelObject.props}
*/ */
static get props() { static get props() {
@ -510,6 +516,12 @@ class Article extends ModelObject {
}; };
} }
/**
* Verbose name for Article model
* @default <tt>'article'</tt>
*/
static get verbose_name() { return 'article'; }
/** /**
* @default {@link Formatters.ArticleFormatter} * @default {@link Formatters.ArticleFormatter}
*/ */
@ -535,22 +547,6 @@ class Article extends ModelObject {
} }
} }
/**
* Node for ModelForest object
* @memberof Models
*/
class TreeNode {
constructor(type, content) {
this.modelname = type;
this.content = content;
this.parent = null;
this.children = [];
}
}
/** /**
* Simple {@link Models.ModelObject} forest. * Simple {@link Models.ModelObject} forest.
* @memberof Models * @memberof Models
@ -558,24 +554,11 @@ class TreeNode {
class ModelForest { class ModelForest {
/** /**
* Dictionary associating types to classes * Abstract structure of the forest
* @abstract * @abstract
* @type {Object} * @type {Object}
*/ */
static get models() { return {}; } static get structure() { return {}; }
/**
* Comparison function for nodes
* @abstract
* @param {class} model Model to use for comparison
* @param {Models.TreeNode} a
* @param {Models.TreeNode} b
* @see {@link Models.ModelObject.compare|ModelObject.compare}
*/
static compare(model, a, b) {
return model.compare(a.content, b.content);
}
/** /**
* Creates empty instance and populates it with data if given * Creates empty instance and populates it with data if given
@ -594,34 +577,38 @@ class ModelForest {
* @param {number} direction * @param {number} direction
*/ */
get_or_create(data, direction) { get_or_create(data, direction) {
var model = this.constructor.models[data.modelname]; var struct_data = this.constructor.structure[data.modelname];
var model = struct_data.model;
var existing = this.find_node(data.modelname, data.content.id); var existing = this.find(data.modelname, data.content.id);
if (existing) { if (existing) {
return existing; return existing;
} }
var content = new this.constructor.models[data.modelname](data.content); var node = new model(data.content);
var node = new TreeNode(data.modelname, content);
if (data.child_sort)
node.child_sort = data.child_sort
if (direction <= 0) { if (direction <= 0) {
if (data.parent) { var parent_name = struct_data.parent;
var parent = this.get_or_create(data.parent, -1); var parent_data = data.parent;
node.parent = parent; var parent_struct = this.constructor.structure[parent_name];
parent.children.push(node); if (parent_data) {
var parent_node = this.get_or_create(parent_data, -1);
node[parent_name] = parent_node;
parent_node[parent_struct.children].push(node);
} else { } else {
this.roots.push(node); this.roots.push(node);
} }
} }
if (direction >= 0 && data.children) { if (direction >= 0) {
for (let child_data of data.children) { var child_name = struct_data.children;
var child = this.get_or_create(child_data, 1); var child_struct = this.constructor.structure[child_name];
child.parent = node; if (data.children && data.children.length) {
node.children.push(child); for (let child_data of data.children) {
var child = this.get_or_create(child_data, 1);
child[child_struct.parent] = node;
node[child_name].push(child);
}
} }
} }
@ -654,26 +641,33 @@ class ModelForest {
* @param {Object} [options] Options for element render method * @param {Object} [options] Options for element render method
*/ */
render_element(node, templates, options) { render_element(node, templates, options) {
var template = templates[node.modelname]; var modelname = node.constructor.verbose_name;
var struct = this.constructor.structure;
var template = templates[modelname];
var options = options || {} ; var options = options || {} ;
var $container = $('<div></div>'); var $container = $('<div></div>');
$container.attr('id', node.modelname+'-'+node.content.id); $container.attr('id', modelname+'-'+node.id);
var $rendered = node.content.display($(template), options); var $rendered = node.display($(template), options);
$container.append($rendered); $container.append($rendered);
//dirty //dirty
node.children.sort(this.constructor.compare.bind(null, this.constructor.models[node.child_sort])); var child_name = struct[modelname].children;
for (let child of node.children) { if (child_name) {
var $child = this.render_element(child, templates, options); node[child_name].sort(struct[modelname].child_sort.compare);
$container.append($child); for (let child of node[child_name]) {
var $child = this.render_element(child, templates, options);
$container.append($child);
}
} }
return $container; return $container;
} }
//TODO adapt
add_to_container($container, node, templates, options) { add_to_container($container, node, templates, options) {
var existing = node.parent ; var existing = node.parent ;
var first_missing = node; var first_missing = node;
@ -696,7 +690,7 @@ class ModelForest {
* @param {Object} [options] Options for element render method * @param {Object} [options] Options for element render method
*/ */
display($container, templates, options) { display($container, templates, options) {
this.roots.sort(this.constructor.compare.bind(null, this.root_sort)); this.roots.sort(this.roots[0].constructor.compare);
for (let root of this.roots) { for (let root of this.roots) {
$container.append(this.render_element(root, templates, options)); $container.append(this.render_element(root, templates, options));
} }
@ -704,27 +698,6 @@ class ModelForest {
return $container; return $container;
} }
/**
* Find if node already exists in given tree
* @param {Models.TreeNode}
*/
find_node(modelname, id) {
var result = null;
function recurse(node) {
if (node.modelname === modelname && node.content.id === id)
result = node;
for (let child of node.children)
recurse(child);
}
for (let root of this.roots)
recurse(root);
return result;
}
/** /**
* Performs for each node (in a DFS order) the callback function * Performs for each node (in a DFS order) the callback function
* on node.content and node.parent.content, if node has given modelname. * on node.content and node.parent.content, if node has given modelname.
@ -732,15 +705,17 @@ class ModelForest {
* @param {function} callback * @param {function} callback
*/ */
traverse(modelname, callback) { traverse(modelname, callback) {
var struct = this.constructor.structure;
function recurse(node) { function recurse(node) {
if (node.modelname === modelname) { if (node.constructor.verbose_name === modelname) {
var parent = node.parent && node.parent.content || null; callback(node);
var children = node.children.map( (child) => child.content);
callback(node.content, children, parent);
} }
for (let child of node.children) var child_name = struct[node.constructor.verbose_name].children;
recurse(child); if (child_name) {
for (let child of node[child_name])
recurse(child);
}
} }
for (let root of this.roots) for (let root of this.roots)
@ -755,9 +730,9 @@ class ModelForest {
find(modelname, id) { find(modelname, id) {
var result = null; var result = null;
function callback(content) { function callback(node) {
if (content.id == id) if (node.id == id)
result = content ; result = node ;
} }
this.traverse(modelname, callback); this.traverse(modelname, callback);
@ -810,9 +785,19 @@ class ArticleList extends APIModelForest {
* @default <tt>{'article': Article, * @default <tt>{'article': Article,
'category': ArticleCategory}</tt> 'category': ArticleCategory}</tt>
*/ */
static get models() { static get structure() {
return {'article': Article, return {
'category': ArticleCategory}; 'article': {
'model': Article,
'parent': 'category',
},
'category': {
'model': ArticleCategory,
'children': 'articles',
'child_sort': Article,
},
};
} }
/** /**
@ -824,15 +809,6 @@ class ArticleList extends APIModelForest {
static get url_model() { static get url_model() {
return Urls['kfet.kpsul.articles_data'](); return Urls['kfet.kpsul.articles_data']();
} }
/**
* Provides model to sort root objects
* {@see Models.ModelForest.constructor|ModelForest.constructor}
*/
constructor() {
super();
this.root_sort = ArticleCategory;
}
} }

View file

@ -599,9 +599,9 @@ class ArticleAutocomplete {
updateDisplay() { updateDisplay() {
var that = this; var that = this;
this.manager.list.traverse('category', function(category, articles) { this.manager.list.traverse('category', function(category) {
var is_active = false; var is_active = false;
for (let article of articles) { for (let article of category.articles) {
if (that.matching.indexOf(article) != -1) { if (that.matching.indexOf(article) != -1) {
is_active = true; is_active = true;
that._$container.find('#article-'+article.id).show(); that._$container.find('#article-'+article.id).show();

View file

@ -1481,7 +1481,6 @@ def kpsul_articles_data(request):
'name': article.category.name, 'name': article.category.name,
'has_addcost': article.category.has_addcost, 'has_addcost': article.category.has_addcost,
}, },
'child_sort': 'article',
} }
}) })
return JsonResponse(articlelist, safe=False) return JsonResponse(articlelist, safe=False)