From df47bedae1c97dc9288c113bd6af0891ee54c367 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 5 Apr 2017 22:10:21 -0300 Subject: [PATCH 1/6] Change ModelForest inner structure --- kfet/static/kfet/js/kfet.api.js | 180 ++++++++++++++------------------ kfet/static/kfet/js/kpsul.js | 4 +- kfet/views.py | 1 - 3 files changed, 80 insertions(+), 105 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 8fccf247..32d8d9f6 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -450,22 +450,28 @@ class ArticleCategory extends ModelObject { /** * Properties associated to a category - * @default ['id', 'name'] + * @default ['id', 'name', 'has_addcost', 'article'] * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { - return ['id', 'name', 'has_addcost']; + return ['id', 'name', 'has_addcost', 'articles']; } /** * Default values for ArticleCategory model instances. - * @default { 'id': 0, 'name': '' } + * @default { 'id': 0, 'name': '', 'has_addcost': true, 'articles': [] } * @see {@link Models.ModelObject.default_data|ModelObject.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 'category' + */ + static get verbose_name() { return 'category'; } + /** * @default {@link Formatters.ArticleCategoryFormatter} */ @@ -490,7 +496,7 @@ class ArticleCategory extends ModelObject { class Article extends ModelObject { /** * Properties associated to an article - * @default ['id', 'name'] + * @default ['id', 'name', 'price', 'stock', 'category'] * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { @@ -510,6 +516,12 @@ class Article extends ModelObject { }; } + /** + * Verbose name for Article model + * @default 'article' + */ + static get verbose_name() { return 'article'; } + /** * @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. * @memberof Models @@ -558,24 +554,11 @@ class TreeNode { class ModelForest { /** - * Dictionary associating types to classes + * Abstract structure of the forest * @abstract * @type {Object} */ - static get models() { 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); - } - + static get structure() { return {}; } /** * Creates empty instance and populates it with data if given @@ -594,34 +577,38 @@ class ModelForest { * @param {number} 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) { return existing; } - var content = new this.constructor.models[data.modelname](data.content); - var node = new TreeNode(data.modelname, content); - - if (data.child_sort) - node.child_sort = data.child_sort + var node = new model(data.content); if (direction <= 0) { - if (data.parent) { - var parent = this.get_or_create(data.parent, -1); - node.parent = parent; - parent.children.push(node); + var parent_name = struct_data.parent; + var parent_data = data.parent; + var parent_struct = this.constructor.structure[parent_name]; + 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 { this.roots.push(node); } } - if (direction >= 0 && data.children) { - for (let child_data of data.children) { - var child = this.get_or_create(child_data, 1); - child.parent = node; - node.children.push(child); + if (direction >= 0) { + var child_name = struct_data.children; + var child_struct = this.constructor.structure[child_name]; + if (data.children && data.children.length) { + 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 */ 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 $container = $('
'); - $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); //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) { - var $child = this.render_element(child, templates, options); - $container.append($child); + if (child_name) { + node[child_name].sort(struct[modelname].child_sort.compare); + for (let child of node[child_name]) { + var $child = this.render_element(child, templates, options); + $container.append($child); + } } return $container; } + //TODO adapt add_to_container($container, node, templates, options) { var existing = node.parent ; var first_missing = node; @@ -696,7 +690,7 @@ class ModelForest { * @param {Object} [options] Options for element render method */ 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) { $container.append(this.render_element(root, templates, options)); } @@ -704,27 +698,6 @@ class ModelForest { 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 * on node.content and node.parent.content, if node has given modelname. @@ -732,15 +705,17 @@ class ModelForest { * @param {function} callback */ traverse(modelname, callback) { + var struct = this.constructor.structure; function recurse(node) { - if (node.modelname === modelname) { - var parent = node.parent && node.parent.content || null; - var children = node.children.map( (child) => child.content); - callback(node.content, children, parent); + if (node.constructor.verbose_name === modelname) { + callback(node); } - for (let child of node.children) - recurse(child); + var child_name = struct[node.constructor.verbose_name].children; + if (child_name) { + for (let child of node[child_name]) + recurse(child); + } } for (let root of this.roots) @@ -755,9 +730,9 @@ class ModelForest { find(modelname, id) { var result = null; - function callback(content) { - if (content.id == id) - result = content ; + function callback(node) { + if (node.id == id) + result = node ; } this.traverse(modelname, callback); @@ -810,9 +785,19 @@ class ArticleList extends APIModelForest { * @default {'article': Article, 'category': ArticleCategory} */ - static get models() { - return {'article': Article, - 'category': ArticleCategory}; + static get structure() { + return { + '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() { return Urls['kfet.kpsul.articles_data'](); } - - /** - * Provides model to sort root objects - * {@see Models.ModelForest.constructor|ModelForest.constructor} - */ - constructor() { - super(); - this.root_sort = ArticleCategory; - } } diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index e1bb2b55..e4623e44 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -599,9 +599,9 @@ class ArticleAutocomplete { updateDisplay() { var that = this; - this.manager.list.traverse('category', function(category, articles) { + this.manager.list.traverse('category', function(category) { var is_active = false; - for (let article of articles) { + for (let article of category.articles) { if (that.matching.indexOf(article) != -1) { is_active = true; that._$container.find('#article-'+article.id).show(); diff --git a/kfet/views.py b/kfet/views.py index 310d06cf..3b3189d0 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1481,7 +1481,6 @@ def kpsul_articles_data(request): 'name': article.category.name, 'has_addcost': article.category.has_addcost, }, - 'child_sort': 'article', } }) return JsonResponse(articlelist, safe=False) From 23d19545a7295cef91669d613902be7cc87dd56f Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 5 Apr 2017 22:23:56 -0300 Subject: [PATCH 2/6] Add back root_sort --- kfet/static/kfet/js/kfet.api.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 32d8d9f6..0f6cd91b 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -690,7 +690,7 @@ class ModelForest { * @param {Object} [options] Options for element render method */ display($container, templates, options) { - this.roots.sort(this.roots[0].constructor.compare); + this.roots.sort(this.constructor.root_sort); for (let root of this.roots) { $container.append(this.render_element(root, templates, options)); } @@ -782,8 +782,6 @@ class ArticleList extends APIModelForest { /** * Default structure for ArticleList instances * @abstract - * @default {'article': Article, - 'category': ArticleCategory} */ static get structure() { return { @@ -800,6 +798,14 @@ class ArticleList extends APIModelForest { } + /** + * Comparison function to sort roots + * @default {@link Models.ArticleCategory.compare|ArticleCategory.compare} + */ + static get root_sort() { + return ArticleCategory.compare; + } + /** * Default url to get ArticlList data * @abstract From 9ba13a81ee1ce6dc7c626ececc70586abf451ef2 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Thu, 6 Apr 2017 00:10:39 -0300 Subject: [PATCH 3/6] Adapt add_to_container + small improvements --- kfet/static/kfet/js/kfet.api.js | 42 +++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 0f6cd91b..889e2152 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -568,6 +568,19 @@ class ModelForest { this.from(datalist || []); } + /** + * Shortcut functions to get parent and children of a given node + */ + get_parent(node) { + var parent_name = this.constructor.structure[node.constructor.verbose_name].parent; + return node[parent_name]; + } + + get_children(node) { + var child_name = this.constructor.structure[node.constructor.verbose_name].children; + return node[child_name]; + } + /** * Fetches an object from the instance data, or creates it if * it does not exist yet.
@@ -642,7 +655,7 @@ class ModelForest { */ render_element(node, templates, options) { var modelname = node.constructor.verbose_name; - var struct = this.constructor.structure; + var struct_data = this.constructor.structure[modelname]; var template = templates[modelname]; var options = options || {} ; @@ -653,12 +666,11 @@ class ModelForest { var $rendered = node.display($(template), options); $container.append($rendered); - //dirty - var child_name = struct[modelname].children; + var children = this.get_children(node); - if (child_name) { - node[child_name].sort(struct[modelname].child_sort.compare); - for (let child of node[child_name]) { + if (children) { + children.sort(struct_data.child_sort); + for (let child of children) { var $child = this.render_element(child, templates, options); $container.append($child); } @@ -667,14 +679,14 @@ class ModelForest { return $container; } - //TODO adapt add_to_container($container, node, templates, options) { - var existing = node.parent ; + var struct = this.constructor.structure; + var existing = this.get_parent(node) ; var first_missing = node; while (existing && !($container.find('#'+existing.modelname+'-'+existing.id))) { - first_missing = existing ; - existing = existing.parent; + first_missing = existing; + existing = this.get_parent(existing); } var $to_insert = render_element(first_missing, templates, options); @@ -705,15 +717,15 @@ class ModelForest { * @param {function} callback */ traverse(modelname, callback) { - var struct = this.constructor.structure; + var that = this; function recurse(node) { if (node.constructor.verbose_name === modelname) { callback(node); } - var child_name = struct[node.constructor.verbose_name].children; - if (child_name) { - for (let child of node[child_name]) + var children = that.get_children(node); + if (children) { + for (let child of children) recurse(child); } } @@ -792,7 +804,7 @@ class ArticleList extends APIModelForest { 'category': { 'model': ArticleCategory, 'children': 'articles', - 'child_sort': Article, + 'child_sort': Article.compare, }, }; From 73fb3c419ed1d5187f9d4706da59a151749da335 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 9 Apr 2017 11:43:51 -0300 Subject: [PATCH 4/6] Add stop check in traverse --- kfet/static/kfet/js/kfet.api.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 889e2152..caee4e85 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -720,18 +720,21 @@ class ModelForest { var that = this; function recurse(node) { if (node.constructor.verbose_name === modelname) { - callback(node); + if callback(node); + return true; } var children = that.get_children(node); if (children) { for (let child of children) - recurse(child); + if (recurse(child)) + return true; } } for (let root of this.roots) - recurse(root); + if (recurse(root)) + return ; } /** @@ -743,8 +746,10 @@ class ModelForest { var result = null; function callback(node) { - if (node.id == id) + if (node.id == id) { result = node ; + return true; + } } this.traverse(modelname, callback); From 9ad208a1718515fe3e0100b7bd9a959cf2b397b9 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 9 Apr 2017 12:30:15 -0300 Subject: [PATCH 5/6] Change child sort + bugfix grom prev commit --- kfet/static/kfet/js/kfet.api.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index caee4e85..19be0607 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -669,7 +669,11 @@ class ModelForest { var children = this.get_children(node); if (children) { - children.sort(struct_data.child_sort); + if (struct_data.child_sort) + children.sort(struct_data.child_sort); + else + children.sort(children[0].constructor.compare); + for (let child of children) { var $child = this.render_element(child, templates, options); $container.append($child); @@ -702,7 +706,11 @@ class ModelForest { * @param {Object} [options] Options for element render method */ display($container, templates, options) { - this.roots.sort(this.constructor.root_sort); + if (this.constructor.root_sort) + this.roots.sort(this.constructor.root_sort); + else + this.roots.sort(this.roots[0].constructor.compare); + for (let root of this.roots) { $container.append(this.render_element(root, templates, options)); } @@ -720,8 +728,9 @@ class ModelForest { var that = this; function recurse(node) { if (node.constructor.verbose_name === modelname) { - if callback(node); + if (callback(node)) { return true; + } } var children = that.get_children(node); @@ -730,6 +739,8 @@ class ModelForest { if (recurse(child)) return true; } + + return false; } for (let root of this.roots) @@ -809,20 +820,11 @@ class ArticleList extends APIModelForest { 'category': { 'model': ArticleCategory, 'children': 'articles', - 'child_sort': Article.compare, }, }; } - /** - * Comparison function to sort roots - * @default {@link Models.ArticleCategory.compare|ArticleCategory.compare} - */ - static get root_sort() { - return ArticleCategory.compare; - } - /** * Default url to get ArticlList data * @abstract From 323f019c0da4936a16062255f595031e4adb2c52 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 9 Apr 2017 13:35:01 -0300 Subject: [PATCH 6/6] Check if children is non empty --- kfet/static/kfet/js/kfet.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 19be0607..78289fa3 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -668,7 +668,7 @@ class ModelForest { var children = this.get_children(node); - if (children) { + if (children && children.length) { if (struct_data.child_sort) children.sort(struct_data.child_sort); else