I am creating a Word document from data using python-docx. I can create all the rows and cells with no problem but, in some cases, when the current record from the database has some content in field comment, I need to add a new line to display a long content.
I tried by appending a paragraph, but the result is that the comment is appended after the table, and I need it to be added bellow the current table row.
I think the solution is to append a table row with all cells merged, but I can't find documentation to do so.
This is the code where I generate the docx file:
class OperationDOCXView(viewsets.ViewSet):
exclude_from_schema = True
def list(self, request):
from ReportsManagerApp.controllers import Operations2Controller
self.profile_id = request.query_params['profile_id']
self.operation_date = request.query_params['operation_date']
self.operation_type = request.query_params['operation_type']
self.format = request.query_params['doc_format']
operation_report_controller = Operations2Controller(self.profile_id, self.operation_date, self.operation_type)
context = operation_report_controller.get_context()
if self.format == 'json':
return Response(context)
else:
word_doc = self.get_operation_word_file(request, context)
return Response("{}{}{}".format(request.get_host(), settings.MEDIA_URL, word_doc))
def get_operation_word_file(self, request, context):
import unicodedata
from django.core.files import File
from django.urls import reverse
from docx import Document
from docx.shared import Inches, Pt
operation_type = {
'arrival': 'Llegadas',
'departure': 'Salidas',
'hotel': 'Hotel-Hotel',
'tour': 'Tours',
}
weekdays = {
'0': 'LUNES',
'1': 'MARTES',
'2': 'MIÉRCOLES',
'3': 'JUEVES',
'4': 'VIERNES',
'5': 'SÁBADO',
'6': 'DOMINGO',
}
titles = ['Booking', 'Nombre', '#', 'Vuelo', 'Hr', 'P Up', 'Traslado', 'Circuito', 'Priv?', 'Agencia', '']
widths = [Inches(1), Inches(2), Inches(0.5), Inches(1), Inches(1), Inches(1), Inches(2), Inches(3), Inches(0.5), Inches(3), Inches(0.5)]
document = Document()
section = document.sections[-1]
section.top_margin = Inches(0.5)
section.bottom_margin = Inches(0.5)
section.left_margin = Inches(0.3)
section.right_margin = Inches(0.2)
style = document.styles['Normal']
font = style.font
font.name ='Arial'
font.size = Pt(10)
company_paragraph = document.add_heading("XXXX TTOO INC")
company_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
description_paragraph = document.add_paragraph("Operación de {} del día {}".format(operation_type[self.operation_type], self.operation_date))
description_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
operation_date = self.get_operation_date().date()
operation_week_day = operation_date.weekday()
day_paragraph = document.add_paragraph(weekdays[str(operation_week_day)])
day_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
for provider_unit, transfers in context.items():
provider_unit_paragraph = document.add_paragraph(provider_unit)
provider_unit_paragraph.style.font.size = Pt(10)
provider_unit_paragraph.style.font.bold = False
table = document.add_table(rows=1, cols=11)
hdr_cells = table.rows[0].cells
runs = []
for i in range(len(hdr_cells)):
runs.append(self.get_hdr_cells_run(hdr_cells[i], titles[i]))
for row in table.rows:
for idx, width in enumerate(widths):
row.cells[idx].width = width
adults = 0
minors = 0
for transfer in transfers:
# table = document.add_table(rows=1, cols=11)
row_cells = table.add_row().cells
row_cells[0].text = transfer['booking']
row_cells[1].text = transfer['people']
row_cells[2].text = transfer['pax']
flight = transfer.get("flight","") if transfer.get("flight","") is not None else ""
row_cells[3].text = flight
flight_time = self.get_flight_time(flight) if flight != '' else ''
row_cells[4].text = flight_time
row_cells[5].text = transfer['pickup_time'].strftime('%H:%M') if transfer['pickup_time'] is not None else ''
row_cells[6].text = transfer['place']
row_cells[7].text = transfer['roundtrip']
row_cells[8].text = transfer['is_private']
row_cells[9].text = transfer['agency']
people = transfer['pax'].split('.')
adults = adults + int(people[0])
minors = minors + int(people[1])
if transfer['comment'] is not None:
document.add_paragraph("Comentarios: {}".format(transfer['comment']))
for row in table.rows:
for idx, width in enumerate(widths):
row.cells[idx].width = width
for cell in row.cells:
paragraphs = cell.paragraphs
for paragraph in paragraphs:
for run in paragraph.runs:
font = run.font
font.size = Pt(8)
row_cells = table.add_row().cells
row_cells[10].text = "{}.{}".format(adults, minors)
current_directory = settings.MEDIA_DIR
file_name = "Operaciones {} {}.docx".format(self.operation_type, self.operation_date)
document.save("{}{}".format(current_directory, file_name))
return file_name
def get_flight_time(self, flight):
from OperationsManagerApp.models import Flight
operation_types = {
'arrival': 'ARRIVAL',
'departure': 'DEPARTURE'
}
operation_date = datetime.strptime(self.operation_date, '%Y-%m-%d')
try:
flight = Flight.objects.get(flight_type=operation_types[self.operation_type], number=flight)
except:
return ''
else:
weekday_times = {
'0': flight.time_monday,
'1': flight.time_tuesday,
'2': flight.time_wednesday,
'3': flight.time_thursday,
'4': flight.time_friday,
'5': flight.time_saturday,
'6': flight.time_sunday,
}
weekday_time = weekday_times[str(operation_date.weekday())]
return weekday_time.strftime('%H:%M') if weekday_time is not None else ''
def get_hdr_cells_run(self, hdr_cells, title):
from docx.shared import Pt
new_run = hdr_cells.paragraphs[0].add_run(title)
new_run.bold = True
new_run.font.size = Pt(8)
return new_run
def get_operation_date(self):
date_array = self.operation_date.split('-')
day = int(date_array[2])
month = int(date_array[1])
year = int(date_array[0])
operation_date = datetime(year, month, day)
return operation_date
One approach is to add a paragraph to one of the cells:
cell.add_paragraph(transfer['comment'])
This will place it in the right position with respect to the row it belongs to, rather than after the table.
If that would take too much room for a single cell that already has another data item in it and you want to add a row, you'll need to account for that when you allocate the table. But assuming you get that worked out, merging the cells is easy:
row.cells[0].merge(row.cells[-1])
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With