""" Helper to generate XLSX Spreadsheets in an uniform way. """ import datetime from datetime import date import openpyxl from openpyxl.styles import NamedStyle, Font, Border, Side DEFAULT_FONT = Font(name='Philosopher', size=10, bold=False, color='000000') THIN_BORDER = Border( bottom=Side(style='thin', color="d3d7cf"), top=Side(style='thin', color="d3d7cf")) XLSX_STYLES = dict() XLSX_STYLES['Content'] = NamedStyle( name='Content', font=DEFAULT_FONT, border=THIN_BORDER ) XLSX_STYLES['Headline'] = NamedStyle( name="Headline", font=openpyxl.styles.Font(name='Philosopher', size=11, bold=True, color='ffffff'), fill=openpyxl.styles.PatternFill(fill_type='solid', start_color='a40000', end_color='a40000') ) XLSX_STYLES['Date'] = NamedStyle( name='Date', font=DEFAULT_FONT, border=THIN_BORDER, number_format='dd.mm.yyyy' ) XLSX_STYLES['Date Time'] = NamedStyle( name='Date Time', font=DEFAULT_FONT, border=THIN_BORDER, number_format='dd.mm.yyyy hh:MM' ) XLSX_STYLES['Float'] = NamedStyle( name='Float', font=DEFAULT_FONT, border=THIN_BORDER, number_format='#,##0.00' ) XLSX_STYLES['Integer'] = NamedStyle( name='Integer', font=DEFAULT_FONT, border=THIN_BORDER, number_format='#,##0' ) def getattr_recursive(obj, attr_string): """A recusive version of gettattr. the attr_string is splitted on the ".". :param obj: a python object. :param attr_string: the desired attribute of the object. :return: a getattr_recursice(obj, 'attr1.attr2') will return the value of attr2 of attr1 from obj """ attr_list = attr_string.split('.') return_value = None for attr in attr_list: return_value = getattr(obj, attr) obj = return_value if isinstance(return_value, datetime.datetime): return_value = return_value.replace(tzinfo=None) return return_value class Workbook(object): workbook = None def __init__(self): """Generate an XLSX Workbook in memory :rtype: object """ self.workbook = openpyxl.Workbook() [self.workbook.add_named_style(style) for style in XLSX_STYLES.values()] [self.workbook.remove(sheet) for sheet in self.workbook.worksheets] def generate_sheet(self, title, columns_settings, object_list, orientation='landscape'): """ :param title: Title of the Sheet :param columns_settings: a list of dicts for the settings of each column :param object_list: List of objects that should be added to the sheet :param orientation: Paper Orientation 'landscape' or 'portrait' """ row = 1 ws = self.workbook.create_sheet() ws.title = title ws.syncHorizontal = True ws.filterMode = True # setup print orientation ws.page_setup.fitToHeight = 0 ws.page_setup.fitToWidth = 1 if orientation == 'landscape': ws.page_setup.orientation = ws.ORIENTATION_LANDSCAPE else: ws.page_setup.orientation = ws.ORIENTATION_PORTRAIT ws.page_setup.paperSize = ws.PAPERSIZE_A4 ws.print_options.horizontalCentered = True # setup page header ws.oddHeader.left.text = title ws.oddHeader.left.size = 14 ws.oddHeader.left.font = "Amerika Sans" ws.oddHeader.left.color = "000000" ws.oddHeader.right.text = str(date.today()) ws.oddHeader.right.size = 14 ws.oddHeader.right.font = "Amerika Sans" ws.oddHeader.right.color = "000000" # write table header for column, data in enumerate(columns_settings, 1): cell = ws.cell(column=column, row=row, value=data['title']) cell.style = 'Headline' row += 1 # write the table content for line in object_list: for column, settings in enumerate(columns_settings, 1): cell = ws.cell(column=column, row=row, value=getattr_recursive(line, settings['attr'])) cell.style = settings['style'] row += 1 # write table footer for column, settings in enumerate(columns_settings, 1): cell = ws.cell(column=column, row=row, value=settings.get('footer')) cell.style = settings['style'] row += 1 # set column widths for settings in columns_settings: ws.column_dimensions[settings['col']].width = settings['width'] def save(self, *args, **kwargs): return self.workbook.save(*args, **kwargs)