XLSXWriter can one use xl_rowcol_to_cell as the cell to act on in conditional formating using type formula? - python-2.7

and thanks in advance for any assistance.
Based on the code snippet below, I'm trying to take the information from SUMMARY_LIST and add it to an xlsx worksheet. I'm then trying to colorize the cells based on 3 conditional formats,
green, text in cell in column A is equal to PASS
yellow, text in cell in column A contains the word PARTIAL and the value in column B of the same row is greater or equal to 6; or the text in cell in column B is equal to CRITICAL
red, text in cell in column A contains the word FAIL and the value in column B if the same row is greater than or equal to 6; or the text in cell in column B is equal to EMERGENCY.
If I hard code the cell in the condition to either $A1 or $B1, I get the desired result, which is that the if my condition passes, then both cells in the row that match either condition will get the appropriate background color.
If I substitute xl_rowcol_to_cell(0,0,col_abs=True) for $A1 and/or xl_rowcol_to_cell(0,1,col_abs=True) for $B1, the cells in my row doesn't get colorized.
In the snippet, I'm trying to colorize the background red if either my cell in column A contains the word "FAIL" and the value in column B is >= 6; OR the cell in column B contains the word "EMERGENCY".
When I run the script, neither the cells in the row containing the word FAIL or the row containing the word EMERGENCY are changed to a red background.
Is there an error in my conditional formatting statement for formatting in red or is it not possible to use the xl_rowcol_to_cell as an input to the condition?
import xlsxwriter
from xlsxwriter.utility import xl_range
from xlsxwriter.utility import xl_range_abs
from xlsxwriter.utility import xl_rowcol_to_cell
SUMMARY_LIST = [{'Item': 'Date', 'Value': '2023-01-11 14:09:16'}, {'Item': 'O/S Version', 'Value': '18.04.6 LTS (Bionic Beaver)'}, {'Item': 'CRITICAL EXPIRY THRESHOLD', 'Value': '6700'}, {'Item': 'EMERGENCY EXPIRY THRESHOLD', 'Value': '720'}, {'Item': 'TOTAL', 'Value': 6}, {'Item': 'PASS', 'Value': 6}, {'Item': 'PARTIAL PASS', 'Value': 6}, {'Item': 'FAIL', 'Value': 6}, {'Item': 'cert1', 'Value': 'EMERGENCY'}, {'Item': 'cert2', 'Value': 'CRITICAL'}]
wb = xlsxwriter.Workbook('myTest.xlsx')
ws = wb.add_worksheet()
row = 0
col = 0
inc = 0
formatEmerg = wb.add_format({'bg_color': 'red'})
formatCrit = wb.add_format({'bg_color': 'yellow'})
formatOk = wb.add_format({'bg_color': 'green'})
for item in SUMMARY_LIST:
inc = 0
for x in item.values():
ws.write(row,col + inc,x )
inc += 1
last_row = row
row += 1
col = 0
ws.conditional_format(xl_range(0,0,last_row,1),
{
'type':'formula',
'criteria':'=OR(and(left(xl_rowcol_to_cell(0,0,col_abs=True),4)="FAIL",(xl_rowcol_to_cell(0,1,col_abs=True)>=6)),left(xl_rowcol_to_cell(0,1,col_abs=True),9)="EMERGENCY")',
'format': formatEmerg
})
ws.conditional_format(xl_range(0,0,last_row,1),
{
'type':'formula',
'criteria':'=OR(and(left($A1,7)="PARTIAL",($B1>=6)),left($B1,8)="CRITICAL")',
'format': formatCrit
})
ws.conditional_format(xl_range(0,0,last_row,1),
{
'type':'formula',
'criteria':'=OR(and(left($A1,4)="PASS",($B1>=6)),left($B1,8)="CRITICAL")',
'format': formatOk
})
wb.close()
I haven't tried anything other than hardcoding the cell, or using the xl_rowcol_to_cell function.
My expectation is that the absolute column, represented by $A1, generated by xl_rowcol_to_cell(0,1,col_abs=True) would be substituted and the condition would evaluate the same as hardcoding the column as $A1. Same for column $B1.

Related

Python-docx: cell edges disappear when merging outer cells

I am using python-docx to create a table with borders on all cells. When I merge cells involving outer cells some outer borders disappear. I use a function from other stackoverflow question -link shown as comment in code below- to set cell borders. How to fix that so outer borders are shown in merged cells?
Wrong borders:
Good borders:
Working example:
from docx import Document
from docx.oxml.shared import OxmlElement, qn
# from https://stackoverflow.com/questions/33069697/how-to-setup-cell-borders-with-python-docx
def set_cell_edges(cell, edges, color, style, width):
"""
Parameter Type Definition
=========== ==================== ==========================================================================================
cell Cell Cell to apply edges
edges str, list, None Cell edges, options are 'top', 'bottom', 'start' and 'end'
color str Edge color
style str Edge style, options are 'single', 'dotted', 'dashed', 'dashdotted' and 'double',
width int, float Edge width in points
"""
kwargs = dict()
for edge in edges:
kwargs[edge] = {'sz': width, 'val': style, 'color': color}
tc = cell._tc
tcPr = tc.get_or_add_tcPr()
# check for tag existance, if none found then create one
tcBorders = tcPr.first_child_found_in("w:tcBorders")
if tcBorders is None:
tcBorders = OxmlElement('w:tcBorders')
tcPr.append(tcBorders)
# list over all available tags
for edge in ('start', 'top', 'end', 'bottom', 'insideH', 'insideV'):
edge_data = kwargs.get(edge)
if edge_data:
tag = 'w:{}'.format(edge)
# check for tag existance, if none found, then create one
element = tcBorders.find(qn(tag))
if element is None:
element = OxmlElement(tag)
tcBorders.append(element)
# looks like order of attributes is important
for key in ["sz", "val", "color", "space", "shadow"]:
if key in edge_data:
element.set(qn('w:{}'.format(key)), str(edge_data[key]))
if __name__ == '__main__':
rows = 3
columns = 3
document = Document()
# create table
table = document.add_table(rows=rows, cols=columns)
# merge cells
scell = table.rows[1].cells[1]
ecell = table.rows[2].cells[2]
scell.merge(ecell)
# set 4 borders in all cells
for row in table.rows:
for cell in row.cells:
set_cell_edges(cell, ['top', 'bottom', 'start', 'end'], '#ff0000', 'single', 1)
document.save('test.docx')
Of course, I can set an extra column and row to set the specific borders. But it would be nice to fix it without that trick. Example with the trick.
Good borders with trick:
if __name__ == '__main__':
rows = 3
columns = 3
document = Document()
# create table
table = document.add_table(rows=rows+1, cols=columns+1)
# merge cells
scell = table.rows[1].cells[1]
ecell = table.rows[2].cells[2]
scell.merge(ecell)
# set 4 borders in all cells
for row in table.rows[:-1]:
for cell in row.cells[:-1]:
set_cell_edges(cell, ['top', 'bottom', 'start', 'end'], '#ff0000', 'single', 1)
# set top border in last row
for cell in table.rows[-1].cells[:-1]:
set_cell_edges(cell, ['top'], '#ff0000', 'single', 1)
# set left border in last column
for cell in table.columns[-1].cells[:-1]:
set_cell_edges(cell, ['start'], '#ff0000', 'single', 1)
document.save('test.docx')

Colour table cells - bokeh

I want to add colour to a row only if a row's cell's value is present in another datasource.
I mean, I have the list A, and the table B, so I want to colour the row X in B if a cell of the row contains a value from A...
I don't even know from where to start..
Pretty much you need to do what ChesuCR mentioned in their comment. To take it one step further, see below a small bokeh application.
If you edit values in the first table, a callback will run and check each 'y' value. An additional column is needed to keep track whether the 'y' values are contained in the seperate list/data source. The value of the additional column is then used to color the cell.
from bokeh.layouts import row
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import DataTable, TableColumn, HTMLTemplateFormatter
from bokeh.io import curdoc
def update_included(attr, old, new):
list_a = [float(a) for a in source_1.data['a']]
ys = source_2.data['y']
y_in = []
for i, y in enumerate(ys):
if y in list_a:
y_in.append(1)
else:
y_in.append(0)
print(ys, y_in, list_a)
source_2.data['y_in'] = y_in
source_1 = ColumnDataSource(data={'a':[1001,1100]})
columns = [
TableColumn(field="a", title="Criteria list")
]
data_table1 = DataTable(source=source_1, columns=columns, width=400, editable=True)
dict1 = {'x':[0]*6,
'y':[0,10,12,13,200,2001],
'y_in':[0]*6}
source_2 = ColumnDataSource(data=dict1)
template="""
<div style="background:<%=
(function colorfromint(){
if(y_in == 1){
return("blue")}
else{return("red")}
}()) %>;
color: white">
<%= value %></div>
"""
formater = HTMLTemplateFormatter(template=template)
columns = [
TableColumn(field="x", title="x"),
TableColumn(field="y", title="y",formatter=formater)
]
data_table2 = DataTable(source=source_2, columns=columns, width=400)
source_1.on_change('data', update_included)
curdoc().add_root(row(data_table1, data_table2))

Python 2.7 xlsxwriter isn't formatting data properly with valign

I have horizontal and vertical alignment set to center, but only the first two columns are centering the data.
The third column appears to be setting the data with a 'top' vertical alignment:
#!/usr/bin/env python
import xlsxwriter
# Create our spreadsheet.
workbook = xlsxwriter.Workbook('stats.xlsx')
worksheet = []
# Open our data file to extract data.
data = open('server_results','r')
# Create a bold centered font for our column headers.
format = workbook.add_format()
format.set_bold()
format.set_align('center')
format.set_align('vcenter')
format.set_text_wrap()
# Non-headers will be centered.
format1 = workbook.add_format()
format1.set_align('center')
format1.set_align('vcenter')
format1.set_text_wrap()
# Provider label/name for worksheet.
worksheet.append(workbook.add_worksheet('Stats'))
# Adjust column widths for data.
worksheet[0].set_column(0,2,25)
#worksheet[0].set_column(1,1,25)
#worksheet[0].set_column(1,2,25)
#worksheet[0].set_row(0,40)
# Start from the first cell. Rows and columns are zero indexed.
worksheet[0].write('A1', 'Column 1', format)
worksheet[0].write('B1', 'Column 2', format)
worksheet[0].write('C1', 'Column 3', format)
row = 1
col = 0
# Count lines
linelist = data.readlines()
count = len(linelist)
# Populate spreadsheet.
for num in range (0, count):
line = linelist[num]
splitline = line.split("\t")
worksheet[0].write(row, col, splitline[0], format1)
worksheet[0].write(row, col + 1, splitline[1], format1)
worksheet[0].write(row, col + 2, splitline[2], format1)
row += 1
#close workbook
workbook.close()
EOF
I've tried modifying how I format the data with following code:
format = workbook.add_format({'bold': True, 'bg_color': 'yellow', 'border': 5, 'align': 'center', 'valign': 'middle', 'text_wrap': True})
but when I do this, then NONE of the columns are centered -- with this format the first two columns are bottom aligned and third column is top aligned
The third column appears to be setting the data with a 'top' vertical alignment
I ran a version of your program with some sample data and I don't see that behaviour:
import xlsxwriter
# Create our spreadsheet.
workbook = xlsxwriter.Workbook('stats.xlsx')
worksheet = []
# Create a bold centered font for our column headers.
format = workbook.add_format()
format.set_bold()
format.set_align('center')
format.set_align('vcenter')
format.set_text_wrap()
# Non-headers will be centered.
format1 = workbook.add_format()
format1.set_align('center')
format1.set_align('vcenter')
format1.set_text_wrap()
# Provider label/name for worksheet.
worksheet.append(workbook.add_worksheet('Stats'))
# Adjust column widths for data.
worksheet[0].set_column(0,2,25)
# Start from the first cell. Rows and columns are zero indexed.
worksheet[0].write('A1', 'Column 1', format)
worksheet[0].write('B1', 'Column 2', format)
worksheet[0].write('C1', 'Column 3', format)
row = 1
col = 0
# Count lines
linelist = ['foo\tbar\tbaz\tbing'] * 5
count = len(linelist)
# Populate spreadsheet.
for num in range (0, count):
line = linelist[num]
splitline = line.split("\t")
worksheet[0].write(row, col, splitline[0], format1)
worksheet[0].write(row, col + 1, splitline[1], format1)
worksheet[0].write(row, col + 2, splitline[2], format1)
row += 1
#close workbook
workbook.close()
The output shows horizontal and vertical centering as expected:
Could you clarify what the issue is.
Using the following:
splitline = [l.rstrip() for l in line.split("\t")]
resolved the issue.

Put information from a dabtabse file into lists

import sqlite3
db = sqlite3.connect('newdb.db')
team_list = ['Munster', 'Leinster', 'Ulster', 'Glasgow']
cursor = db.cursor()
for i in range(len(team_list)):
team_names = team_list[i].upper()
searchStr = '%' + team_names + '%'
cursor.execute('select * from tickets where Name LIKE ?', (searchStr,))
teams_points = cursor.fetchall()
print teams_points
cursor.close()
db.close()
This is my python code used to display all data in the table 'tickets' in newdb.db. I have a list with the team names and i want to be able to search these team names in the database and calculate information on tickets sold.
picture of database
[(u'MUNSTER', 5, u'First Round'), (u'MUNSTER', 5, u'First Round'),
(u'MUNSTER', 8, u'Second Round'), (u'MUNSTER', 10, u'Both Rounds')]
[(u'LEINSTER', 2, u'Second Round'), (u'LEINSTER', 16, u'First Round'),
(u'LEINSTER', 5, u'Both Rounds'), (u'LEINSTER', 6, u'Both Rounds'),
(u'LEINSTER', 3, u'First Round')]
[(u'ULSTER', 10, u'Second Round')]
[(u'GLASGOW', 4, u'First Round')]
Above is my output when I run the script, i want to be able put each team into a list as
team_list=['team_name', 'total first round tickets', 'second round tickets']
munster_list = ['MUNSTER', '20', '18']
leinster_list = ['LEINSTER','30','13']
ulster_list = ['ULSTER','0','10']
glasgow_list = ['GLASGOW','4','0']
so then to print the list I can just use print munster_list
Use GROUP BY to get one output row from the rows in each group. Use CASE expressions to sum up only certain values:
SELECT Name,
sum(CASE WHEN Type IN ('First Round', 'Both Rounds')
THEN Amount
ELSE 0
END) AS "first round tickets",
sum(CASE WHEN Type IN ('Second Round', 'Both Rounds')
THEN Amount
ELSE 0
END) AS "second round tickets"
FROM tickets
GROUP BY Name
ORDER BY Name;

Converting certain index values in list to int

I have searched for a solution to this, but I have not been able to find one, weirdly.
I am opening a file with the following contents in it.
Alex,10,0,6,3,7,4
Bob, 6,3,7,2,1,8
I want to convert all the values in score_list from 1-4 index value to an integer. I have tried to do so with this following but it just doesn't work.
score_list = []
def opening_file():
counter = 0
with open('scores.txt', newline='') as infile:
reader = csv.reader(infile)
for row in reader:
score_list.append(row[0:5])
counter = 0
while counter != 5:
counter +=1
row[counter] = int(row[counter])
print (score_list)
opening_file()
but it doesn't work and just produces
[['Alex', '10', '0', '6', '3'], ['Bob', ' 6', '3', '7', '2']]
instead of [['Alex', 10, 0, 6, 3], ['Bob', 6, 3, 7, 2]]
You are converting the items within row which is just a throwaway variable. Also you don't need to that redundant works, you can simply unpack your row to name and scores parts and use a list comprehension in order to convert the digits to integer.
with open('scores.txt', newline='') as infile:
reader = csv.reader(infile)
for row in reader:
name, *scores = row
score_list.append([name] + [int(i) for i in scores])
Your while loop transforming the values in row happens too late. Each row's values have already been copied (by a slice operation) into a new list which has been appended to score_list. And you only run the loop on the last row anyway (assuming your indentation in the question is correct).
Try something like this:
with open('scores.txt', newline='') as infile:
reader = csv.reader(infile)
for row in reader:
for i in range(1,5):
row[i] = int(row[i])
score_list.append(row[0:5])
I'm using a for loop on a range, rather than a while loop, just because it's more convenient (a while loop version could work just fine, it just requires more lines). The key thing is to change row inside the loop on reader and before we slice the row to append to score_list.
First of all, the code converts items in the row array, but you print the score_list array. Second, as it alters the row variable outside the reader for loop, it only alters the last row. You could do something like this:
import csv
def opening_file():
with open('scores.txt', newline='') as infile:
return [[row[0]] + [int(x) for x in row[1:]] for row in csv.reader(infile)]
score_list = opening_file()
print(str(score_list))