python Reportlab, add two tables side by side in pdf - python-2.7

I am trying to insert two tables in the pdf using python reportlab.
Number of rows in the second table are less than first table.
But the horizontal position of the two tables is not matching.
Some empty space coming on the second table.
How can I remove this space so that both the tables will come horizontally aligned?
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter, inch
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
doc = SimpleDocTemplate("simple_table_grid.pdf", pagesize=letter)
elements = []
data= [['00', '01', '02', '03', '04'],
['10', '11', '12', '13', '14'],
['20', '21', '22', '23', '24'],
['30', '31', '32', '33', '34']]
data1= [['00', '01', '02', '03', '04'],
['10', '11', '12', '13', '14']]
t1=Table(data,5*[0.4*inch], 4*[0.4*inch],hAlign='LEFT')
t1.setStyle(TableStyle([('ALIGN',(1,1),(-2,-2),'RIGHT'),
('TEXTCOLOR',(1,1),(-2,-2),colors.red),
('VALIGN',(0,0),(0,-1),'TOP'),
('TEXTCOLOR',(0,0),(0,-1),colors.blue),
('ALIGN',(0,-1),(-1,-1),'CENTER'),
('VALIGN',(0,-1),(-1,-1),'MIDDLE'),
('TEXTCOLOR',(0,-1),(-1,-1),colors.green),
('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
('BOX', (0,0), (-1,-1), 0.25, colors.black),
]))
t2=Table(data1,5*[0.4*inch], 2*[0.4*inch],hAlign='RIGHT')
t2.setStyle(TableStyle([('ALIGN',(1,1),(-2,-2),'RIGHT'),
('TEXTCOLOR',(1,1),(-2,-2),colors.red),
('VALIGN',(0,0),(0,-1),'TOP'),
('TEXTCOLOR',(0,0),(0,-1),colors.blue),
('ALIGN',(0,-1),(-1,-1),'CENTER'),
('VALIGN',(0,-1),(-1,-1),'MIDDLE'),
('TEXTCOLOR',(0,-1),(-1,-1),colors.green),
('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
('BOX', (0,0), (-1,-1), 0.25, colors.black),
]))
t = [[t1,t2]]
temp = Table(t)
elements.append(temp)
doc.build(elements)

To have 2 tables horizontal, i don't think it's possible to do it with reportLab.
I kind had the same problem, i found a way to do resolve it.
I create only one table to represent both tables, and i hide the grid between the 2, so it look like it's 2 tables. With that solution you can position the 2 tables the way you want.
My setStyle() looks like that
t.setStyle(TableStyle([
('GRID', (0,0), (4,1), 0.5, colors.black),
('ALIGN',(1,1),(-1,-1),'CENTER'),
('BACKGROUND',(0,0),(4,0),colors.lightgrey),
('BACKGROUND',(3,3),(3,5),colors.lightgrey),
('BOX',(3,3),(3,5),0.5,colors.black),
('BOX',(4,3),(4,5),0.5,colors.black)]))

well, there is a workaround for it.
You can add your tables in another table with two columns and a single row.
Something like this..
data= [(t1, t2)]
t = Table(data, [250, 250])
t.setStyle(TableStyle([
('VALIGN',(0,0),(-1,-1),'TOP')
]))

Related

Obtain one list of dictionaries out of a list containing list of dictionaries

I have a list containing a list of dictionaries.
There are 29 different years, each year has 20 dictionaries where each dictionary constitutes one specific word field and contains words and each word has one value.
I want to have a dictionary for each word field in which each word has 29 values (one for each year).
So instead of
[[{'Apple': 0.4, 'Banana': 0.6}, {'Chocolate': 0.5, 'Chips': 0.5}],
[{'Apple': 0.6, 'Banana': 0.4}, {'Chocolate': 0.6, 'Chips': 0.4}],
[{'Apple': 0.8, 'Banana': 0.2}, {'Chocolate': 0.1, 'Chips': 0.9}]]
I want something like this:
[[{'Apple': [0.4, 0.6, 0.8], 'Banana': [0.6, 0.4, 0.2]}, {'Chocolate': [0.5, 0.6, 0.1], 'Chips': [0.5, 0.4, 0.9]}]
I've been trying for hours and wanted to use a defaultdict like
totaldict = defaultdict(list)
for d in (topics[0]): # create dictionary for first word field
for key, value in d.items():
totaldict[key].append(value)
but in that case the list topics would need to be structured like this:
[[{'Apple': 0.4, 'Banana': 0.6}, {'Apple': 0.6, 'Banana': 0.4}, {'Apple': 0.8, 'Banana': 0.2}],
[{'Chocolate': 0.5, 'Chips': 0.5},{'Chocolate': 0.6, 'Chips': 0.4}, {'Chocolate': 0.1, 'Chips': 0.9}]]
There's probably an easy way to restructure my list but I have no idea how. How can I do that? Or maybe there is another way of achieving a single dictionary for each year containing multiple values.
Transforming the list can be done by using a numpy array.
topics = np.array(topics)
totaldict = defaultdict(list)
for d in (topics[:,0]): # create dictionary for first word field
for key, value in d.items():
totaldict[key].append(value)

How to position a python Reportlab table at position 10,100 and use drawString

I am a python hobbyist and reportlab newbie.
I know how to use drawString to put text at a particular place on a page, e.g.:
c.drawString(10,100,"Welcome to Reportlab!")
But I can't figure out how to place a table (which will only be a few lines long) so that the table begins at the same position as the c.drawString(10,100,"Welcome to Reportlab!"), which I will place elsewhere if I learn how to put my table there instead.
I also can't figure out how to use drawString in the same script, since using a canvas is the only way I know how to use the drawString functioning. My 4 lines of canvas code (following this paragraph) would close the canvas/file and create the PDF. The table code (further below) also closes the file and builds the PDF, and I don't see how to use the "doc.build(elements)" line to close the canvas which I use for drawString operations.
c = canvas.Canvas(r"e:\hellonu.pdf", pagesize=letter)
c.setFont("Courier", 9) #choose your font type and font size
c.drawString(10,60,"Welcome to Reportlab!")
c.save()
I would appreciate any guidance you could give me (1) about how to place the table so that it begins at 10,100, and (2) how to use drawString in the same script. If some of my code does nothing useful, please don't assume I put it there intentionally; I tried to copy enough from examples, so that my table would have wordwrap functioning.
Here's the code I have been playing with:
# http://zewaren.net/site/node/139
from reportlab.lib import colors
from reportlab.lib.pagesizes import LETTER, inch, portrait
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib.styles import getSampleStyleSheet
doc = SimpleDocTemplate(r"e:\test_report_lab.pdf", pagesize=LETTER, rightMargin=30,leftMargin=30, topMargin=30,bottomMargin=18)
doc.pagesize = portrait(LETTER)
elements = []
data = [
["Directory"],
["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA "],
]
style = TableStyle([('ALIGN',(1,1),(-2,-2),'RIGHT'),
('TEXTCOLOR',(1,1),(-2,-2),colors.red),
('VALIGN',(0,0),(0,-1),'TOP'),
('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
('BOX', (0,0), (-1,-1), 0.25, colors.black),
])
#Configure style and word wrap
s = getSampleStyleSheet()
s = s["BodyText"]
s.wordWrap = 'CJK'
data2 = [[Paragraph(cell, s) for cell in row] for row in data]
t=Table(data2)
t.setStyle(style)
#Send the data and build the file
elements.append(t)
doc.build(elements)
Lately, I stumbled across the same issue. The problem here is that in reportlab, tables are so-called "flowables" whereas the drawString command is "fixed".
I found a solution thanks to this great tutorial written by Mike Driscoll: "Reportlab: Mixing Fixed Content and Flowables".
Here is a slighty adapted version which constitutes a working snippet:
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import mm
from reportlab.pdfgen import canvas
from reportlab.platypus import Image, Paragraph, Table
from reportlab.lib import colors
c = canvas.Canvas('example.pdf', pagesize=A4) # alternatively use bottomup=False
width, height = A4
data = [[1, 2, 3], [2, 1, 3], [3, 2, 1]]
table = Table(data, colWidths=10*mm)
table.setStyle([("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("ALIGN", (0,0), (-1,-1), "CENTER"),
('INNERGRID', (0,0), (-1,-1), 0.25, colors.black)])
table.wrapOn(c, width, height)
table.drawOn(c, 0*mm, 5*mm)
styles = getSampleStyleSheet()
ptext = "This is an example."
p = Paragraph(ptext, style=styles["Normal"])
p.wrapOn(c, 50*mm, 50*mm) # size of 'textbox' for linebreaks etc.
p.drawOn(c, 0*mm, 0*mm) # position of text / where to draw
c.save()
I can also recommend two more tutorials by Mike Driscoll, which have allowed me to get quickly familiar with reportlab.
A Simple Step-by-Step Reportlab Tutorial
Reportlab Tables – Creating Tables in PDFs with Python
Thanks a lot, Mike!

Delete vertical lines (or vertical rules) in plt.table? Let only horizontal lines as a Table not as a chart

Is there any way to remove the vertical lines in a matplotlib table?
Or even using text.usetex=True obtain a plot (using Arial for all texts and numbers) and append a table without vertical lines?
My code is this:
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("ticks")
plt.rcParams.update({'xtick.labelsize' : 9,
'ytick.labelsize' : 9,
'mathtext.fontset' : 'stixsans',
'mathtext.default': 'regular'
})
plt.subplots_adjust(left=0.17, right=0.96, top=0.97, bottom=0.09)
fig = plt.figure(figsize=(5.5, 3.4))
ax = fig.add_subplot(111)
ax.plot([1, 2, 3, 4, 5, 6])
ax.set_xlabel('X Label')
ax.set_ylabel('Unit ('+u'μ'+r'velocity $\cdot$ m$^{-2}$ s$^{-1}$)',
size=10)
l1 = ["","t0", "t1", "t2", "t3 ", "t4", "t5", "t6"]
l2 = ["DLI", 35, 38, 10, 22, 25, 85, 22]
t = ax.table(
colWidths=[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1],
cellText = [l1, l2],
loc="upper center",
colLoc="center",
rowLoc="center",
)
plt.savefig('mpl_table.png')
plt.show()
I tried to include some LaTeX stuff in another plot, which preserves Arial font, but does not render the table with horizontal lines Table correct. When I get the table as I want, the fonts can not be setted to Arial.
Here the codes show the plots in two steps. First generating the first plot and after running in a new session the second plot.
My main problem is to maintain the Arial font (mandatory rules in the guide for authors) and tables should be without vertical rules, as stated on first plot.
I have here three approachs and no one satisfy those recquirements (e.g. Arial font and table with no vertical lines)
Any clues?
Cheers,
Arnaldo.

Arranging nested tuples

I know that this is probably a silly question and I apologize for that, but I am very new to python and have tried to solve this for a long time now, with no success.
I have a list of tuples similar to the one bellow:
data = [('ralph picked', ['nose', '4', 'apple', '30', 'winner', '3']),
('aaron popped', ['soda', '1', 'popcorn', '6', 'pill', '4', 'question', '29'])]
I would like to sort the nested list in descending other:
data = [('ralph picked', ['apple', '30', 'nose', '4', 'winner', '3']),
('aaron popped', ['question', '29', 'popcorn', '6', 'pill', '4', 'soda', '1'])]
I tried using simple
sorted(data)
but what I get is only the first item of tuple sorted. What I am missing here? I really thank you for any help.
Let's consider only the inner list. The first issue is that it seems like you want to keep word, number pairs together. We can use zip to combine them, remembering that seq[::2] gives us every second element starting at the 0th, and seq[1::2] gives us every second starting at the first:
>>> s = ['nose', '4', 'apple', '30', 'winner', '3']
>>> zip(s[::2], s[1::2])
<zip object at 0xb5e996ac>
>>> list(zip(s[::2], s[1::2]))
[('nose', '4'), ('apple', '30'), ('winner', '3')]
Now, as you've discovered, if you call sorted on a sequence, it sorts first by the first element, then by the second to break ties, etc., going as deep as it needs to. So if we call sorted on this:
>>> sorted(zip(s[::2], s[1::2]))
[('apple', '30'), ('nose', '4'), ('winner', '3')]
Well, that looks like it works, but only by fluke because apple-nose-winner is in alphabetical order. Really we want to sort by the second term. sorted takes a key parameter:
>>> sorted(zip(s[::2], s[1::2]), key=lambda x: x[1])
[('winner', '3'), ('apple', '30'), ('nose', '4')]
That didn't work either, because it's sorting the number strings lexicographically (dictionary-style, so '30' comes before '4'). We can tell it we want to use the numerical value, though:
>>> sorted(zip(s[::2], s[1::2]), key=lambda x: int(x[1]))
[('winner', '3'), ('nose', '4'), ('apple', '30')]
Almost there -- we want this reversed:
>>> sorted(zip(s[::2], s[1::2]), key=lambda x: int(x[1]), reverse=True)
[('apple', '30'), ('nose', '4'), ('winner', '3')]
And this is almost right, but we need to flatten it. We can use either a nested list comprehension:
>>> s2 = sorted(zip(s[::2], s[1::2]), key=lambda x: int(x[1]), reverse=True)
>>> [value for pair in s2 for value in pair]
['apple', '30', 'nose', '4', 'winner', '3']
or use itertools.chain:
>>> from itertools import chain
>>> list(chain.from_iterable(s2))
['apple', '30', 'nose', '4', 'winner', '3']
And I think that's where we wanted to go.

Regex to eliminate SQL insert values with multiple lines

I'm migrating some BDD to a new structure and I need to make some changes in the structure. To do it, I start with a backup with the insert commands and using sublimetext2 and RegReplace I create some script to adapt the inserts.
The problem I have is when I need to delete one of the column's values and some of the data is text that can be in multiple lines and I have multiple inserts.
I'm using this regex:
(.*table.*VALUES \(.*,)(.*,)(([\s\S]*,){12})(.*;)
Replace by: \1\3\5
And this is the data:
INSERT INTO table (cola, colb, colc, cold, cole, colf, colg, colg, colh, coli, colj, colk, coll, colm, coln, colo, colp, colq, colr, cols, colt, culu) VALUES (1, '2', 3, NULL, '5', 6, '7', '8', 9, NULL, NULL, 12, '13', '14', '15', '16', '17', '18', '19', 20, '21', 22');
INSERT INTO table (cola, colb, colc, cold, cole, colf, colg, colg, colh, coli, colj, colk, coll, colm, coln, colo, colp, colq, colr, cols, colt, culu) VALUES (1, '2', 3, NULL, '5', 6, '7', '8', 9, NULL, NULL, 12, '13', '14', '
15
', '16', '17', '18', '19', 20, '21', '22');
If I use the regex with just one line it will eliminate the column number 9, but when I code it in sublimetext2 or input both lines together or more it will not work because it doesn't separate both INSERT INTO statements.
Here is the example not working
Thanks for your help :)
Do you try with ungreedy quantifiers:
(.*?table.*?VALUES \((?:[\s\S]*?,){14})([\s\S]*?,)(([\s\S]*?,)*?)(.*?;)
(if you want to test it with regex101 don't forget to add the g modifier)
notice:
with RegReplace, you can set greedy to false and remove all the question marks.
RegReplace support the dotall modifier thus you can replace [\s\S] by . and add (?s) before
example with the 2 notices:
(?s)(.*table.*VALUES \((?:.*,){14})(.*,)((.*,)*)(.*;)