<file_sep># -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-12-10 16:12
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        ('menu_app', '0001_initial'),
    ]

    operations = [
        migrations.AlterField(
            model_name='menuitem',
            name='category',
            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='menu_item_to_category', to='menu_app.Category'),
        ),
        migrations.AlterField(
            model_name='menuitem',
            name='product',
            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='menu_item_product', to='menu_app.Product'),
        ),
    ]
<file_sep># dj_server
Django server
<file_sep>from django.utils import timezone

def logme(obj_to_log,type='0'):
	'''
	console log functiom
	'''
	print ('++++++++ ' + '{:%y.%m.%d %H:%M:%S}'.format(
		timezone.now()) + ' [{}] '.format(type) + repr(obj_to_log))

def add_li(list_to_add,li_txt):
	list_to_add.append('<li class="tree_li">')
	list_to_add.append(li_txt)
	list_to_add.append('</li>')

def attr_to_string(obj):
	ret = []
	for attr, value in obj.__dict__.items():
		ret.append('{}='.format(attr))
		ret.append('{} '.format(value))
	return ''.join(ret)

def obj_w_level_to_list(list_to_add, obj, level_to_add):
	'''
	Add obj to a given list and level
	'''
	obj.level = level_to_add
	obj.level_string = (get_tabulating_string(level_to_add))
	list_to_add.append(obj)

def get_tabulating_string(level):
	# child_level = child.get_level()
	if level > 1:						
		return '<span class="space">{}</span>- '.format('--' * (level-1))
	# li_list.append('- ')
	else:
		return '- '

<file_sep>from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.index,name='index'),  
    url(r'^insch/(?P<node>\d+)/(?P<is_cat>\d{1})/(?P<rel_id>\d+)/$', 
    	views.insert_child, name='insert_child'), 
	url(r'^inssib/(?P<node>\d+)/(?P<is_cat>\d{1})/(?P<rel_id>\d+)/$', 
    	views.insert_sibling, name='insert_sibling'), 
	url(r'^cat/(?P<cat_id>\d+)/$', 
    	views.category_card, name='category_card'), 
	url(r'^prod/(?P<prod_id>\d+)/$', 
    	views.product_card, name='product_card'), 
	url(r'^del/(?P<element_id>\d+)/$', 
    	views.delete_element, name='delete_element'),
	url(r'^generate_tree/$', 
    	views.generate_tree, name='generate_tree'),
    url(r'^delete_tree/$', 
        views.delete_tree, name='delete_tree'),
    url(r'^menu_admin/$', 
        views.menu_admin, name='menu_admin'),
    url(r'^get_childs_li/(?P<parent_id>\d+)/$', 
        views.get_childs_li, name='get_childs_li'),
]<file_sep># -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-12-11 15:31
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        ('menu_app', '0003_auto_20161210_1716'),
    ]

    operations = [
        migrations.CreateModel(
            name='MenuTree',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('is_category', models.BooleanField()),
                ('bot_sib', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='bot_sib_to_self', to='menu_app.MenuTree')),
                ('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='menu_tree_to_category', to='menu_app.Category')),
                ('first_child', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='first_child_to_self', to='menu_app.MenuTree')),
                ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='parent_to_self', to='menu_app.MenuTree')),
                ('product', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='menu_tree_product', to='menu_app.Product')),
                ('top_sib', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='top_sib_to_self', to='menu_app.MenuTree')),
            ],
        ),
    ]
<file_sep># -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-12-10 17:16
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('menu_app', '0002_auto_20161210_1612'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='menuitem',
            name='item_type',
        ),
        migrations.AddField(
            model_name='menuitem',
            name='is_category',
            field=models.BooleanField(default=False),
            preserve_default=False,
        ),
    ]
<file_sep># -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-12-22 17:41
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('menu_app', '0005_menutree_child_qty'),
    ]

    operations = [
        migrations.AddField(
            model_name='menutree',
            name='child_qty_active',
            field=models.IntegerField(default=0),
        ),
    ]
<file_sep>from django.contrib import admin

from .models import Category, Product, MenuTree

admin.site.register(Category)
admin.site.register(Product)
admin.site.register(MenuTree)
<file_sep>//js scripts for menu_app

function getCategory(){
    $input = $("input#category_input");
    val = $input.val();
    list = $input.attr('list'),
    match = $('#' + list + ' option').filter(function() {
       return ($(this).val() === val);
    });
    if(match.length > 0) {
        return match.data('id');
    };
    return;    
};

function getProduct(){
    $input = $("input#product_input");
    val = $input.val();
    list = $input.attr('list'),
    match = $('#' + list + ' option').filter(function() {
       return ($(this).val() === val);
    });
    if(match.length > 0) {
        return match.data('id');
    };
    return;    
};

function getChildCheckbox(){
    chbox = $('input#child_checkbox');
    return chbox.prop('checked');    
};

function getCategoryRadio(){
    cat_radio = $('input#category_radio');
    return cat_radio.prop('checked'); 
};

function createMenuEntry(){
    $('div#change_menu').toggleClass('invisible','visible');
    category_id = getCategory();
    product_id = getProduct();
    create_child = getChildCheckbox();
    create_category = getCategoryRadio();
};

function expand_pressed(self){
    //Expend category button pressed    
    var btn = $(self);
    var parent = btn.parent();
    var request = '/get_childs_li/' + btn.data('id') + '/'; 
    if (btn.data('state') == 'closed') {
        $.ajax({                    
            url: request,
            method: "GET",            
            success: function(result){
                if (result.slice(0,5) == 'Error') {
                    if (btn.attr('id') != 'expand_root') {
                        alert(result);     
                    }                                       
                } else {
                    parent.append(result);
                };
            },
            error: function(result){ 
                alert(result);               
            }

        });
        btn.data('state', 'open');     
    } else {
        li_to_remove = $("li", $(parent));
        li_to_remove.remove();
        btn.data('state', 'closed');  
    };
};

$('a.insert_btn').on("click", function(e){
    //Insert button in admin_menu pressed
    var a = $(this);
    var child_chbox = $('input#child_checkbox');
    $('span#change_menu_entry_id').text(a.data('id'));        
    $('div#change_menu').toggleClass('invisible', false);
    if (Boolean(a.data('iscategory'))) {
        child_chbox.toggleClass('invisible', false);
        child_chbox.prop('checked', true);
    } else {
        child_chbox.toggleClass('invisible', true); 
        child_chbox.prop('checked', false); 
    };
});

$('button#change_insert_btn').on("click", function(e){
    //Insert button on modification menu pressed
    var btn = $(this);
    var request = ''        
    category_id = getCategory();
    product_id = getProduct();
    create_child = getChildCheckbox();
    create_category = getCategoryRadio();

    if (create_child) {
        request += '/insch/';
    } else {
        request += '/inssib/';
    };

    request += $('span#change_menu_entry_id').text() + '/';

    if (create_category) {
        if (!category_id) {
            alert('Category is not set');
            return;
        }
        request += '1/' + category_id + '/';            
    } else {
        if (!product_id) {
            alert('Product is not set');
            return;
        }
        request += '0/' + product_id + '/';
    };
    $.ajax({                    
        url: request,
        method: "GET",
        success: function(result){
            if (result.slice(0,5) == 'Error') {
                alert(result);
            } else {
                location.reload();
            };
        },
        error: function(result){ 
            alert(result);               
        }
    });         
    $('div#change_menu').toggleClass('invisible', true);
});

$(document).ready(function(){ 

    //Expands root element
    var btn = $('a#expand_root');
    expand_pressed(btn);
});
<file_sep>from django.db import models
from django.db.models import F
from django.core import exceptions
from .add_lib import logme, add_li, attr_to_string, obj_w_level_to_list
import random
import datetime

class Category(models.Model):
	name = models.TextField(max_length=50)
	active = models.BooleanField(default=False)

	def save(self, *args, **kwargs):
		if self.name == 'root':
			try:
				root = Category.objects.filter(name='root')
			except:
				root = None
			if root:
				return False				
		super(Category, self).save(*args, **kwargs)

class Product(models.Model):
	name = models.TextField(max_length=50)
	price = models.DecimalField(max_digits=10, decimal_places=2)
	active = models.BooleanField(default=False)

class MenuTree(models.Model):
	parent = models.ForeignKey('self', related_name='parent_to_self',
							   blank=True, null=True, db_index=True)
	first_child = models.ForeignKey('self', related_name='first_child_to_self',
		                       blank=True, null=True, db_index=True)
	top_sib = models.ForeignKey('self', related_name='top_sib_to_self',
						       blank=True, null=True, db_index=True)
	bot_sib = models.ForeignKey('self', related_name='bot_sib_to_self',
							   blank=True, null=True, db_index=True)
	is_category = models.BooleanField()
	category = models.ForeignKey(Category, related_name='menu_tree_to_category', 
							   blank=True, null=True)
	product = models.ForeignKey(Product, related_name='menu_tree_product',
							   blank=True, null=True)
	child_qty = models.IntegerField(default=0)
	child_qty_active = models.IntegerField(default=0)

	def update_parents_element_count(self, changes): #, active):
		'''
		Updates child_qty and child_qty_active fields for all parents
		changes = [child_qty,child_qty_active]
		'''	
		self.child_qty = F('child_qty') + changes[0]
		self.child_qty_active = F('child_qty_active') + changes[1]
		self.save()
		self.refresh_from_db()			
		if self.parent:			
			self.parent.update_parents_element_count(changes)	
		return	

	def get_root(self):
		'''
		Returns root element, creates if does not exist, with category name 'root'
		'''
		try:
			root = MenuTree.objects.get(parent=None)
		except exceptions.ObjectDoesNotExist:
			root = MenuTree()
			root.is_category = True
			try:
				cat = Category.objects.filter(name='root')[0]
				if not cat.active:
					cat.active = True
					cat.save()
			except:
				cat = Category()
				cat.name = 'root'
				cat.active = True
				cat.save()
			root.category = cat	
			root.save()
		return root

	def insert_child(self, child_is_category, link_id, *args, **kwargs):
		"""
		Always inserts first child, pushing ex_first_child down if exist
		link_id = id of Category or Product models
		"""
		if not self.is_category:
			raise exceptions.FieldError("Child can be inserted only in category")

		child = MenuTree()
		child.parent = self
		child.is_category = bool(int(child_is_category))
		if child.is_category:
			try:
				cat = Category.objects.get(id=int(link_id))
			except exceptions.ObjectDoesNotExist:	
				raise exceptions.FieldError('Existing Category must be set')	
			child.category = cat
		else:
			try:
				prod = Product.objects.get(id=int(link_id))
			except exceptions.ObjectDoesNotExist:	
				raise exceptions.FieldError('Existing Product must be set')	
			child.product = prod

		if self.first_child:
			child.bot_sib = self.first_child
			child.save()
			self.first_child.top_sib = child
			self.first_child.save()			
		else:
			child.bot_sib = None
			child.save()

		self.first_child = child
		if not child.is_category:
			if child.product.active and self.category.active and not self.parent_category_inactive():
				self.update_parents_element_count([1,1])
			else:
				self.update_parents_element_count([1,0])
		else:
			self.save()
		return child

	def insert_sibling(self, sibling_is_category, link_id, *args, **kwargs):
		"""
		Always inserts sibling after self
		"""	
		if not self.parent:
			raise exceptions.FieldError('Root element can not have siblings')

		sibling = MenuTree()
		sibling.parent = self.parent
		sibling.first_child = None
		sibling.top_sib = self
		sibling.is_category = bool(int(sibling_is_category))
		if sibling.is_category:
			try:
				cat = Category.objects.get(id=int(link_id))
			except exceptions.ObjectDoesNotExist:	
				raise exceptions.FieldError('Category must be set')	
			sibling.category = cat
		else:
			try:
				prod = Product.objects.get(id=int(link_id))
			except exceptions.ObjectDoesNotExist:	
				raise exceptions.FieldError('Product must be set')	
			sibling.product = prod

		sibling.bot_sib = self.bot_sib #None or object
		sibling.save()

		if self.bot_sib:
			self.bot_sib.top_sib = sibling
			self.bot_sib.save()

		self.bot_sib = sibling	
		self.save()

		#updates child qtys in parent entry		
		if not sibling.is_category:
			if sibling.product.active and sibling.parent.parent_category_inactive():
				sibling.parent.update_parents_element_count([1,1])
			else:
				sibling.parent.update_parents_element_count([1,0])

		return sibling	

	def parent_category_inactive(self):
		'''
		Checks if exist inactive parent category
		'''
		parent = self.parent
		while parent:
			if not parent.category.active:
				return True
			parent = parent.parent
		return False

	def delete_category_childs(self):
		'''
		Returns a list of deleted child_qty and child_qty_active
		'''
		deleted_products = [0,0]
		run_loop = True
		while run_loop:		
			if self.first_child:
				try:
					child = MenuTree.objects.get(id=self.first_child.id)
				except exceptions.ObjectDoesNotExist:
					child = None
					self.first_child = child
					run_loop = False

				if child:
					self.first_child = child.bot_sib
					deleted_products = [x + y for x, y in 
										zip(deleted_products, child.delete_element(False))]			
			else:
				run_loop = False
		return deleted_products

	def delete_element(self, update_parents):
		'''
		Deletes element and childs if category
		returns a list of deleted child_qty and child_qty_active quantities
		'''
		deleted_products = [0,0]
		if self.top_sib:
			self.top_sib.bot_sib = self.bot_sib
			self.top_sib.save()
		if self.bot_sib:
			self.bot_sib.top_sib = self.top_sib
			self.bot_sib.save()
	
		if self.is_category:			
			deleted_products = self.delete_category_childs()
			if not self.category.active or self.parent_category_inactive():
				deleted_products[1] = 0 #active products
		else: #product	
			if self.product.active and not self.parent_category_inactive():
				deleted_products = [1,1]
			else:
				deleted_products = [1,0]

		if self.parent:
			if self.parent.first_child == self: #self is a first child
				self.parent.first_child = self.bot_sib
				self.parent.save()
			if update_parents and (deleted_products[0] != 0):
				self.parent.update_parents_element_count([-1 * x for x in deleted_products])

		self.parent = None 
		self.top_sib = None
		self.bot_sib = None
		self.delete()				
		return deleted_products

	def get_tree(self):	
		'''
		Retruns tree objects list
		'''
		tree = []
		root = self.get_root()
		obj_w_level_to_list(tree, root, 0)
		tree += root.get_category_branch()
		return tree		

	def get_category_branch(self, level=0, *args, **kwargs):
		'''
		Returns list of MenuTree objects, that are childs of self
		'''			
		local_level = level + 1
		branch = []
		if self.first_child:
			run_loop = True
			while run_loop:
				if self.first_child.is_category:
					obj_w_level_to_list(branch,self.first_child,local_level)
					branch += self.first_child.get_category_branch(local_level)
				else:
					obj_w_level_to_list(branch,self.first_child,local_level)
				if not self.first_child.bot_sib:  #last child element
					run_loop = False
				else:
					self.first_child = self.first_child.bot_sib
		return branch

	def get_single_branch(self):
		'''
		Returns single level child elements
		'''
		branch = []
		if self.first_child:
			run_loop = True
			while run_loop:
				obj_w_level_to_list(branch,self.first_child,self.get_level()+1)
				if not self.first_child.bot_sib:  #last child element
					run_loop = False
				else:
					self.first_child = self.first_child.bot_sib
		return branch

	def generate_random_tree(self):
		'''
		Generates total_elements random tree 
		'''
		total_cats = 300
		total_prods = 10000
		total_elements = 500

		start = datetime.datetime.now()
		logme('generate_random_tree process started {}'.format(start))
		existing_cats = Category.objects.count()
		if existing_cats < total_cats:
			for i in range(total_cats - existing_cats):
				new_cat = Category()
				new_cat.name = 'Category_auto_{}'.format(i)
				new_cat.active = random.choice([True,True,True,False])
				new_cat.save()

		existing_prods = Product.objects.count()
		if existing_prods < total_prods:
			for i in range(total_prods - existing_prods):
				new_prod = Product()
				new_prod.name = 'Product_auto_{}'.format(i)
				new_prod.active = random.choice([True,True,True,False])
				new_prod.price = random.randrange(1000) + random.randrange(100)/100
				new_prod.save()

		root = self.get_root()
		elements_left = total_elements
		for i in range(total_elements):
			elements_left -= root.create_random_child(elements_left,1)
			if elements_left < 1:
				break
		end = datetime.datetime.now()
		logme('generate_random_tree process finished, duration {}, elements created {}'.format(end - start,total_elements))
			
	def create_random_child(self, elements_left, level):
		'''
		Creates random child, if child is category makes recursion call 
		to generate its childs until elements_left == created_childs
		'''
		stop_prob = 0.2
		prod_prob = 0.8 #else category
		prod_in_first_level_prob = 0.1		
		cats = Category.objects.all()
		prods = Product.objects.all()
		created_childs = 0
		for i in range(elements_left):
			if (random.random() <= stop_prob) or (created_childs > elements_left):
				break
			elif level == 1:
				insert_prod = random.random() <= prod_in_first_level_prob
			else:
				insert_prod = random.random() <= prod_prob
			if insert_prod:
				child = self.insert_child(False,random.choice(prods).id)
				created_childs += 1
			else: #category
				cat = self.insert_child(True,random.choice(cats).id)
				created_childs += 1
				created_childs += cat.create_random_child(elements_left - created_childs, level + 1)
		return created_childs

	def get_level(self):
		'''
		Returns level of self element
		'''
		level = 0
		parent = self.parent
		while parent:
			level += 1
			parent = parent.parent
		return level
<file_sep>from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.core.exceptions import ObjectDoesNotExist, FieldError

from .add_lib import logme, add_li, get_tabulating_string
from .models import Category, Product, MenuTree

def index(request):
	'''
	Non editable menu view
	'''
	resp = []
	root = MenuTree()
	root = root.get_root()
	# tree = root.get_tree()
	tree = root.get_single_branch()	
	elem_qty = len(tree) + 1
	li_tag = '<li class="tree_li"'
	return render(request, 'menu_app/menu_active.html',{'tree': tree,
														'elem_qty': elem_qty,
														'root': root,
														'li_tag': li_tag})

def insert_child(request, node, is_cat, rel_id):
	'''
	Inserts child for parent node
	is_cat (is_category) = 0 or 1
	url like: /insch/78/1/1/
	'''
	try:
		parent = MenuTree.objects.get(id=node)	
		parent.insert_child(is_cat,rel_id)
	except Exception as e:
		return HttpResponse('Error: ' + str(e))
	return HttpResponse('Ok: child created')

def insert_sibling(request, node, is_cat, rel_id):
	'''
	Inserts sibling for node element
	is_cat (is_category) = 0 or 1
	url like: /inssib/78/0/10/
	'''
	try:
		top_sibling = MenuTree.objects.get(id=node)
		top_sibling.insert_sibling(is_cat,rel_id)
	except FieldError as e:
		return HttpResponse('Error: ' + str(e))
	return HttpResponse('Ok: sibling created')

def category_card(request, cat_id):
	'''
	Shows category card view
	'''
	try:
		cat = Category.objects.get(id=int(cat_id))
	except ObjectDoesNotExist:	
		return HttpResponse('<h1>Category Does Not Exist</h1>')	
	resp = []
	resp.append('<h1>')
	resp.append(cat.name)
	resp.append('</h1>')
	resp.append('<p>Active ')
	resp.append(str(cat.active))
	resp.append('</p>')
	return HttpResponse(''.join(resp))

def product_card(request, prod_id):
	'''
	Shows product card view
	'''
	try:
		prod = Product.objects.get(id=int(prod_id))
	except ObjectDoesNotExist:	
		return HttpResponse('<h1>Category Does Not Exist</h1>')	
	resp = []
	resp.append('<h1>')
	resp.append(prod.name)
	resp.append('</h1>')
	resp.append('<p>Active: ')
	resp.append(str(prod.active))
	resp.append(' Price: ')
	resp.append(str(prod.price))
	resp.append('</p>')
	return HttpResponse(''.join(resp))

def delete_element(request, element_id):
	'''
	Deletes tree element
	'''	
	try:
		menu = MenuTree.objects.get(id=int(element_id))
	except ObjectDoesNotExist:	
		return redirect('menu_admin')
	menu.delete_element(True)
	return redirect('menu_admin')		

def generate_tree(request):
	'''
	Generates random tree
	'''
	root = MenuTree()
	root = root.get_root()
	root.generate_random_tree()
	return redirect('menu_admin')

def menu_admin(request):
	'''
	Editable version menu view
	'''
	root = MenuTree()
	root = root.get_root()
	tree = root.get_tree()
	cats_inactive = 0
	prods_inactive = 0
	active_prods_in_inactive_cats = 0
	prods_total = 0
	for item in tree:
		if item.is_category:
			if not item.category.active:
				cats_inactive += 1
		else:
			prods_total += 1
			if not item.product.active:
				prods_inactive += 1
			elif item.parent_category_inactive():
				active_prods_in_inactive_cats += 1

	elements_total = len(tree)
	cats_total = elements_total - prods_total
	cats = Category.objects.all()
	prods = Product.objects.all()
	return render(request, 
		'menu_app/menu.html',{'tree': tree, 
							  'elements_total': elements_total,
							  'cats': cats,
							  'prods': prods,
							  'cats_inactive': cats_inactive,
							  'prods_inactive': prods_inactive,
							  'prods_total': prods_total,
							  'cats_total': cats_total,
							  'active_prods_in_inactive_cats': active_prods_in_inactive_cats})

def delete_tree(request):
	'''
	Deletes whole tree
	'''
	all_obj = MenuTree.objects.all()
	all_obj.delete()
	return redirect('menu_admin')

def wrap_in_li(li_list,tree_obj):
	'''
	Wraps MenuTree obj in li and adds to list
	'''
	wrap_element = False
	if tree_obj.is_category:			
		if tree_obj.category.active:
			wrap_element = True	
	else:
		if tree_obj.product.active:
			wrap_element = True
	if wrap_element:		
		li_list.append('<li class="tree_li" id="li_{}">'.format(tree_obj.id))
		li_list.append(get_tabulating_string(tree_obj.get_level()))	
		li_list.append(str(tree_obj.id))
		if tree_obj.is_category:
			li_list.append(' <a class="expand_btn" href="javascript:void(0)" \
				data-id="{}" data-state="closed" onclick="expand_pressed(this)\
				">+</a>'.format(tree_obj.id))		
			li_list.append(' <a class="category" \
				href="/cat/{}">{}</a>'.format(tree_obj.category.id,tree_obj.category.name))
			li_list.append(' ({})'.format(tree_obj.child_qty_active))
		else:
			li_list.append(' <a class="product" \
				href="/prod/{}">{}</a>'.format(tree_obj.product.id,tree_obj.product.name))
			li_list.append(' (€ {})'.format(tree_obj.product.price))
		li_list.append('</li>')	

def get_childs_li(request, parent_id):
	'''
	Returns category childs wraped in <li> elements
	'''
	li_list = []
	try:
		child = MenuTree.objects.get(parent=parent_id, top_sib=None)
	except:
		child = None
	while child:
		wrap_in_li(li_list,child)
		child = child.bot_sib	
	if len(li_list) == 0:
		return HttpResponse('Error: Category has no child elements')
	else:
		return HttpResponse(''.join(li_list))
		
