Output nested list to xlsx using Django - django

I have a complex nested list of models that looks like this:
hierarchy_tree = ['0000', 'hierarchy' [['0000-22', 'hierarchy2', [['0000-33', 'hiearchy3', [['0000-44-4444', 'hiearchy4', [['0000-55-5555-55', 'hiearchy5', []]]]]]]]]]
I am able to easily display this in a template using dot notation - example:
{% for hierarchy in hierarchy_tree %}
<tr class="item" data-id="{{system.0}}" data-parent="">
<td>
{{hierarchy.0}}
</td>
<td>
{{hierarchy.1.genericname}}
</td>
Now I am trying to output this to an .xlsx file but I cannot figure out how to pass all of the levels of this list? How can I do the same thing that I did in the template to pass this list to excel?
I have tried the following which will return the 1st list but throws an error (ValueError at /post/1/export/hierarchy/ - cannot covert(my passed in list)to excel) for the sublists because of the way that they are nested I believe.
for r in hierarchy_tree:
ws.append(r)
I have also tried and failed repeatedly to access the sublists using other methods.
So bottom line I need to figure out how to access and pass the values for the sublists - any ideas or help will be greatly appreciated?
Thank you

Comment: ... is just 1 row in the list ...
Python print a instance of Type list in one line, surounding with [ ... ].
Your hierarchy_tree is of Type list of n lists.
Every n't list in hierarchy_tree starts with [ and ends with ].
You have to break your hierarchy_tree into row Data.
For instance:
def treeWalk(tree, level=0):
rData = [ '' for i in range(level)]
for item in tree:
if isinstance(item, list):
if len(rData) > level:
ws.append(rData)
level += 1
treeWalk(item, level)
return
rData.append(item)
treeWalk(hierarchy_tree)
Tested with Python:3.4.2 - openpyxl:2.4.1 - LibreOffice: 4.3.3.2

Related

django getlist() function

I have a tags generator for my blog site and every time I enter tags it generates a hidden html input, and it appends whenever I insert a tag like this example below.
<input type="hiiden" name="tags" value="<value of inserted tags>">
<input type="hiiden" name="tags" value="<value of inserted tags>">
<input type="hiiden" name="tags" value="<value of inserted tags>">
In my Django view I get all the tags from the form through request.POST.getlist("tags") and save it on database so the inserted tags is like this on database ['value1','value2','value3'] When I fetch the tags in the Django template through for loop to extract the strings from the arrayfield the output shows like this:
[ ' v a l u e 1 ' , ' v a l u e 2 ' , ' v a l u e 3 ' ] // one by one character output
the code works fine but the problem is it outputs character by character in the array including the brackets and the apostrophe and the commas. What i want to achieve is only the strings which are present in the array field
anyone knows what can be the solution for this? any suggestion and help is appreciated.

How to identify gaps in a sequential list using Django templates

If I have an ordered sequential list in my django template:
my_list = [
(1, "Billy Holiday"),
(2, "Louis Armstrong"),
# Number 3 is missing!
(4, "Ella Fitzgerald"),
(5, "Frank Sinatra"),
]
And I want to show something like this in my html:
1. Billy Holiday
2. Louis Armstrong
-- Some rows may be missing --
4. Ella Fitzgerald
5. Frank Sinatra
Is there a clever way to do this? I'm trying to accomplish this using Django templates. The idea would be to look at the previous iteration of the loop, and identify if rows are missing based on the counter values.
It seems like you try to move some logic from views to templates. I would strongly advise against this approach. It is not testable, harder to maintain, most likely will conflict with DRY principle.
So just doing the work in the view would probably be better.
However, if you are totally adamant that you want this done in template, you can make your own template filter:
someapp/templatetags/app_tags.py
from django import template
register = template.Library()
#register.filter
def set_missing_items(collection):
new_list = []
last = 0
for item in collection:
if item[0] != last + 1:
new_list.append('-- Some rows may be missing --')
new_list.append(item)
last = item[0]
return new_list
And then use it as follows:
template.html
{% load app_tags %}
{% for item in list|set_missing_items %}
{{ item }}
{% endif %}
Make sure to follow all steps from the documentation to make your custom filters work (like having proper folder structure with __init__.py files, making sure that app that has those filters is installed, etc).
Maybe a simple approach will be build the list this way:
my_list = [
(1, "Billy Holiday"),
(2, "Louis Armstrong"),
(3, ""),
(4, "Ella Fitzgerald"),
(5, "Frank Sinatra"),
]
and write a if for take care of the blank data inside the loop.

Loop through an array django - slightly more complex

I am using a JQuery chart library where you pass values a format like [0,2,5,9].
I have an array in my views where I currently access each index to return the value rest 1 = arr[0], rest 2 = arr[1] ... and then passing these values from the view into my HTML page and inserting the values for the chart like [res1,res2]. This is not feasible because I never know the size of the array so it'd be a constant manual approach of accessing the array. Is there a way I easily loop through each one?
Slight problem. Currently after accessing each index I convert value to an int. So I don't think I'd be able to do it via looping through the html page - it'd have to be done via views. Unless I can somehow call conversion function defined in the view in the html page?
--- views ---
array
-> returns multiple string values i.e. name = paul, name = john
-> paul = arr[0]
-> function(paul['']) converts it to a string.
--- html page ---
{{paul}}
ideally i'd like to do:
[rather than refer to each index here just loop through all array values.. but somehow calling my conversion function too on each value else what I want to do won't work. ]
{% for values in array %}
[insert each one here and call the convert int function from views]
I might misunderstood your question, but sounds like you just want to pass a list of string to the template, loop on each item in the list and do some conversion. I'm not really sure which step did you get stuck but the simplest way is to do everything in the views.py:
views.py
def view_func(request):
array = [{'Speed': 2, 'Height': 1, 'PersonID': 1}, {'Speed': 2, 'Height': 1, 'PersonID': 1}]
# do the conversion on each value in the list
converted_array = [int(i['speed']]) for i in array]
context = {'array': array, 'converted_array': converted_array}
template:
<!-- to loop on original array -->
{% for value in array %}
{{ value }}
{% endfor %}
<!-- to loop on the converted array -->
{% for value in converted_array %}
{{ value }}
{% endfor %}

Construct Xpath

I have the following repeated piece of the web-page:
<div class="txt ext">
<strong class="param">param_value1</strong>
<strong class="param">param_value2</strong>
</div>
I would like to extract separately values param_value1 and param_value2 using Xpath. How can I do it?
I have tried the following constructions:
'//strong[#class="param"]/text()[0]'
'//strong[#class="txt ext"]/strong[#class="param"][0]/text()'
'//strong[#class="param"]'
none of which returned me separately param_value1 and param_value2.
P.S. I am using Python 2.7 and the latest version of Scrapy.
Here is my testing code:
test_content = '<div class="txt ext"><strong class="param">param_value1</strong><strong class="param">param_value2</strong></div>'
sel = HtmlXPathSelector(text=test_content)
sel.select('//div/strong[#class="param"]/text()').extract()[0]
sel.select('//div/strong[#class="param"]/text()').extract()[1]
// means descendant or self. You are selecting any strong element in any context. [...] is a predicate which restricts your selection according to some boolean test. There is no strong element with a class attribute which equals txt ext, so you can exclude your second expression.
Your last expression will actually return a node-set of all the strong elements which have a param attribute. You can then extract individual nodes from the node set (use [1], [2]) and then get their text contents (use text()).
Your first expression selects the text contents of both nodes but it's also wrong. It's in the wrong place and you can't select node zero (it doesn't exist). If you want the text contents of the first node you should use:
//strong[#class="param"][1]/text()
and you can use
//strong[#class="param"][2]/text()
for the second text.

Python function misbehaves when called multiple times

I wrote a function in Python that will create a simple four column table in HTML. When I call it from file, it returns the table correctly.
Issues arise, however, if it is called multiple times in a single script. The first one appears as it ought to. The second time it is called, all of the rows beneath the title row have six columns (two blank) instead of four. The third time, there are ten columns (six blank).
I only started coding recently, so I don't know very much about what's going on behind the scenes here.
When a function is called twice or more times in succession, is a new instance of the function called? Are the variables all 'reset' so to speak?
This is the code of the called function:
def fourColumnTable(title1, list1, title2, list2, title3, list3, title4, list4):
error = 0
#Check that the lists are all of the same length
if(len(list1) != len(list2) or len(list1) != len(list3) or len(list1) != len(list4)):
error = 1
table = "ERROR: The lists must all be the same length"
if(error == 0):
tableList = []
#Append <table> tag
tableList.append('<table class="table table-bordered">')
#Format list elements and titles
#Put each title inside <th> tags
titleList = []
titleList.append(title1)
titleList.append(title2)
titleList.append(title3)
titleList.append(title4)
for i in range(len(titleList)):
titleList[i] = "<th>" + str(titleList[i]) + "</th>"
#Put each string element inside <td> tags
for i in range(len(list1)):
list1[i] = "<td>" + str(list1[i]) + "</td>"
for i in range(len(list2)):
list2[i] = "<td>" + str(list2[i]) + "</td>"
for i in range(len(list3)):
list3[i] = "<td>" + str(list3[i]) + "</td>"
for i in range(len(list4)):
list4[i] = "<td>" + str(list4[i]) + "</td>"
#Put all list elements in the tableList
tableList.append('<thead>')
for i in range(len(titleList)):
tableList.append(titleList[i])
tableList.append('</thead>')
tableList.append('<tbody>')
for i in range(len(list1)):
tableList.append('<tr>')
tableList.append(list1[i])
tableList.append(list2[i])
tableList.append(list3[i])
tableList.append(list4[i])
tableList.append('</tr>')
tableList.append('</tbody>')
#Close the <table> tag
tableList.append('</table>')
#Assign tableList to one variable
table = ''.join(tableList)
return table
I suspect that some (but not all) of your listN arguments being reused between calls. This leads to your bug, because your code modifies the provided lists each time it is called. It adds <td> and </td> around each list item. If you repeatedly do this on the same list, the items will end up with multple nested tags, e.g. <td><td><td>...</td></td></td>.
This then gets rendered as extra empty columns, as your browser fills in the missing closing tags between the repeated <td> opening tags and ignores the extra closing tags at the end.
A first quick fix would be to create a new list with the modified items, rather than modifying the provided list (here using a list comprehension):
list1 = ["<td>" + item + "</td>" for item in list1]
A further improvement would be to use a library to create your table, rather than creating it by string manipulation yourself. There are a variety of XML templating libraries you could use, but I don't have enough experience with any of them to make a strong suggestion. This page might be a good place to start browsing.