Bugzilla – Attachment 184438 Details for
Bug 152804
(python generated) TABLES: Numbers in a cell equal to or greater than 10 are set to 0 upon saving and re-opening
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
The Python script used to generate the .odt file
make-invoice.py (text/x-python), 8.19 KB, created by
Mitch
on 2023-01-02 04:14:00 UTC
(
hide
)
Description:
The Python script used to generate the .odt file
Filename:
MIME Type:
Creator:
Mitch
Created:
2023-01-02 04:14:00 UTC
Size:
8.19 KB
patch
obsolete
>import argparse >import csv >import dateutil.parser as dparser >from datetime import datetime >import fileinput >from pathlib import Path >import xml.etree.ElementTree as ElementTree >import tempfile >import zipfile > >def read_hour_rows(csv_file_path): > with open(csv_file_path, 'r') as csvfile: > reader = csv.reader(csvfile, quotechar='"') > data = [] > row_index = 0 > for row in reader: > column_count = len(row) > if (column_count != 3): > raise ValueError("In csv file " + csv_file_path + ", row at index " + str(row_index) > + " has " + str(column_count) + " columns instead of 3: \n" + str(row)) > ># print(str(len(row)) + "_" + str(row) + "_") > > data += [row] > row_index += 1 > return data > ># Based on https://crcok.wordpress.com/2014/10/25/unzipping-editing-and-zipping-odt-documents-in-python/. ># Extracts content.xml from odt file, modifies it, writes it back and re-zips it into a new odt. > >arg_parser = argparse.ArgumentParser() >arg_parser.add_argument('invoice_number') >args = arg_parser.parse_args() > ># A temporary directory to store the files we will extract from the odt. >tmp_dir_url = tempfile.gettempdir() + "/invoice-template" ># The directory that contains this script >this_dir_path = Path(__file__).parent.resolve() > ># This is one of the files that will be extracted when we unzip the odt. >xml_file_url = tmp_dir_url + "/content.xml" > >template_odt_path = str(this_dir_path / 'invoice-template.odt') >invoice_odt_path = str(this_dir_path / 'invoice-') + str(args.invoice_number) + ".odt" > ># ># Unzip ODT ># >print(" -- Extracting ---------------------") >print("%s -> %s" % (template_odt_path, tmp_dir_url)) > ># Load the template into memory. >zipdata = zipfile.ZipFile(template_odt_path) ># Extract its contents into the file in the temporary directory. >zipdata.extractall(tmp_dir_url) > > ># ># Find and replace tokens ># >print(" -- Replacing -------------") >print(xml_file_url) > >xml_namespaces = { 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0' } >xml_tree = ElementTree.parse(xml_file_url) >xml_tree_root = xml_tree.getroot() > > ># Set the invoice number and date in InvoiceNumberTable. >invoice_number_tables = list(xml_tree_root.iterfind(".//*[@table:name='InvoiceNumberTable']", xml_namespaces)) >if (len(list(invoice_number_tables)) == 0): > raise ValueError("Can't find InvoiceNumberTable") > >invoice_number_table = invoice_number_tables[0] >if (len(invoice_number_table) != 4): > raise ValueError("Expected 4 rows in InvoiceNumberTable, but got " + str(len(invoice_number_table))) > > >invoice_number_row = invoice_number_table[2] >if ("table-row" not in invoice_number_row.tag): > raise ValueError("Expected row at index 2 to be table-row " + invoice_number_row.tag) > >invoice_number_cell = invoice_number_row[1] >if ("table-cell" not in invoice_number_cell.tag): > raise ValueError("Expected table-cell for row at index 2 but got " + invoice_number_cell.tag) > >invoice_number_p = invoice_number_cell[0] >if ("p" not in invoice_number_p.tag): > raise ValueError("Expected p for row at index 2 but got " + invoice_number_p.tag) > >invoice_number_p.text = args.invoice_number > > >invoice_data_row = invoice_number_table[3] >if ("table-row" not in invoice_data_row.tag): > raise ValueError("Expected row at index 3 to be table-row " + invoice_data_row.tag) > >invoice_data_cell = invoice_data_row[1] >if ("table-cell" not in invoice_data_cell.tag): > raise ValueError("Expected table-cell for row at index 3 but got " + invoice_data_cell.tag) > >invoice_data_p = invoice_data_cell[0] >if ("p" not in invoice_data_p.tag): > raise ValueError("Expected p for row at index 3 but got " + invoice_data_p.tag) > >invoice_data_p.text = datetime.today().strftime('%d/%m/%Y') > > ># Look for HoursTable. ># Convert it into a list because otherwise there is a warning about generators not being subscriptable. >hours_tables = list(xml_tree_root.iterfind(".//*[@table:name='HoursTable']", xml_namespaces)) >if (len(list(hours_tables)) == 0): > raise ValueError("Can't find HoursTable") > >hour_table = hours_tables[0] > ># Add the data we read from hours.csv into each cell. >hours_csv_file_path = this_dir_path / 'hours.csv' >hour_rows = read_hour_rows(hours_csv_file_path) >hour_row_count = len(hour_rows) >xml_table_row_count = len(list(hour_table)) >if (hour_row_count > xml_table_row_count): > raise ValueError("Too many hours (" + str(len(hour_rows)) + ")" > + " to fit into invoice template table (" + str(xml_table_row_count) + ")") > ># Gather all of the rows so that row_index starts from the first row we're interested in. >rows = [] >for row in hour_table: > if ("table-row" in row.tag): > rows.append(row) > ># Add data to cells. >row_index = 0 >for row in rows: ># print("looping on index " + str(row_index)) > > if (row_index >= hour_row_count): > # We reached the last hour row. > break > ># print("- looking at row index " + str(row_index) + ", tag is: " + row.tag) > > if ("table-row" in row.tag): > if (len(list(row)) != 3): > raise ValueError("Expected row to have three children") > > cell_index = 0 > for cell in row: ># print(" - looking at cell index " + str(cell_index)) > if ("table-cell" not in cell.tag): > raise ValueError("Expected table-cell to be a child of table-row") > > p = cell[0] > > if ("p" not in p.tag): > raise ValueError("Expected p to be a child of table-cell") > > p.text = hour_rows[row_index][cell_index] ># print(" - setting text of row " + str(row_index) + " cell " + str(cell_index) + " to " + str(p.text)) > > cell_index += 1 > row_index += 1 > > > ># Set the total hours/amount formula since us unzipping it seems to screw it up... >summary_tables = list(xml_tree_root.iterfind(".//*[@table:name='SummaryTable']", xml_namespaces)) > >if (len(list(summary_tables)) == 0): > raise ValueError("Can't find SummaryTable") > >summary_number_table = summary_tables[0] ># Bit nasty hard-coding these, but since the layout won't change: first three items are table-column, next three are table-row. >if (len(summary_number_table) != 6): > raise ValueError("Expected 6 rows in SummaryTable, but got " + str(len(summary_number_table))) > ># We're interested in the rows. The first one contains the cell that stores the number of total hours. >total_hours_row = summary_number_table[3] >if ("table-row" not in total_hours_row.tag): > raise ValueError("Expected row at index 3 to be table-row " + total_hours_row.tag) > ># The 3rd column is the right-most one that stores the number of total hours. >total_hours_cell = total_hours_row[2] >if ("table-cell" not in total_hours_cell.tag): > raise ValueError("Expected table-cell but got " + total_hours_cell.tag) > >for key in total_hours_cell.attrib: > if ("formula" in key): > # Apparently the preceding '=' is added automatically. > total_hours_cell.attrib[key] = "sum<HoursTable.C2:C24>" > ># The last row contains the cell that stores the total amount due. >total_amount_due_row = summary_number_table[5] >if ("table-row" not in total_amount_due_row.tag): > raise ValueError("Expected row at index 5 to be table-row " + total_amount_due_row.tag) > ># The 3rd column is the right-most one that stores the total amount due. >total_amount_due_cell = total_amount_due_row[2] >if ("table-cell" not in total_amount_due_cell.tag): > raise ValueError("Expected table-cell but got " + total_amount_due_cell.tag) > >for key in total_amount_due_cell.attrib: > if ("formula" in key): > total_amount_due_cell.attrib[key] = "<SummaryTable.C1>*<SummaryTable.C2>" > > ># Write the data back in. >xml_tree.write(str(xml_file_url)) > > ># Zip contents of the temporary directory to ODT ># Use file list from the original archive ># This preserves the file structure in the new Zip file ># The most important is that the "mimetype" is the first file in archive > >print(" -- Compressing --------------------") >print("%s -> %s" % (tmp_dir_url , invoice_odt_path)) > >with zipfile.ZipFile(invoice_odt_path, 'w') as outzip: > zipinfos = zipdata.infolist() > for zipinfo in zipinfos: > file_name = zipinfo.filename # The name and path as stored in the archive > file_url = tmp_dir_url + "/" + file_name # The actual name and path > outzip.write(file_url, file_name)
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 152804
:
184435
|
184436
|
184437
| 184438