def set_columns_width(self, begin, end, start_cell): """ Копирование ширины колонок :param begin: начало секции, пример ('A', 1) :type begin: 2-tuple :param end: конец секции, пример ('E', 6) :type end: 2-tuple :param start_cell: ячейка с которой выводилась секция :type start_cell: 2-tuple """ # определим интервал столбцов из которых надо взять ширину end = self.get_cell_end(end) cols = list(ColumnHelper.get_range(begin[0], end[0])) # определим интервал столбцов, начинаемый со столбца начальной ячейки, куда надо прописать ширину end_col = ColumnHelper.add(start_cell[0], ColumnHelper.difference(end[0], begin[0])) new_cols = list(ColumnHelper.get_range(start_cell[0], end_col)) # запишем ширину столбцов в интервал for index, src_col in enumerate(cols): dst_col = new_cols[index] src_col_el = self._create_or_get_output_col(src_col) # если нет исходной колонки, то не надо копировать if not src_col_el is None: # копируем данные attrib_col = dict(src_col_el.items()) dst_col_el = self._create_or_get_output_col( dst_col, attrib_col) # записываем в новую колонку self._set_new_column_width(dst_col, src_col_el, dst_col_el)
def cell_dimensions(section, merge_cell, start_cell): """ Получение координаты ячейки после смещения из-за объединения ячеек :param section: начало секции :type section: 2-tuple :param merge_cell: начало объединенной ячейки :type merge_cell: 2-tuple :param start_cell: стартовая ячейка :type start_cell: 2-tuple """ section_begin_col, section_begin_row = section start_col, start_row = start_cell begin_col, begin_row = merge_cell new_begin_row = start_row + begin_row - section_begin_row new_begin_col = ColumnHelper.add( start_col, ColumnHelper.difference(begin_col, section_begin_col)) return new_begin_col + str(new_begin_row)
def set_columns_width(self, begin, end, start_cell): """ Копирование ширины колонок :param begin: начало секции, пример ('A', 1) :type begin: 2-tuple :param end: конец секции, пример ('E', 6) :type end: 2-tuple :param start_cell: ячейка с которой выводилась секция :type start_cell: 2-tuple """ # определим интервал столбцов из которых надо взять ширину end = self.get_cell_end(end) cols = list(ColumnHelper.get_range(begin[0], end[0])) # определим интервал столбцов, начинаемый со столбца начальной ячейки, куда надо прописать ширину end_col = ColumnHelper.add(start_cell[0], ColumnHelper.difference(end[0], begin[0])) new_cols = list(ColumnHelper.get_range(start_cell[0], end_col)) # запишем ширину столбцов в интервал for index, src_col in enumerate(cols): dst_col = new_cols[index] src_col_el = self._create_or_get_output_col(src_col) # если нет исходной колонки, то не надо копировать if not src_col_el is None: # копируем данные attrib_col = dict(src_col_el.items()) dst_col_el = self._create_or_get_output_col(dst_col, attrib_col) # записываем в новую колонку self._set_new_column_width(dst_col, src_col_el, dst_col_el)
def test_horizontal(self, report=None): if report is None: return for i in range(3): section3 = report.get_section('Section3') section3.flush({'section3': i}, oriented=Section.HORIZONTAL) self.assertEqual(section3.sheet_data.cursor.row, ('C', 9)) self.assertEqual(section3.sheet_data.cursor.column, (ColumnHelper.add('G', 2 * i), 7))
def set_dimension(self): u""" Установка диапазона """ _, row_index = self.cursor.row col_index, _ = self.cursor.column dimension = 'A1:%s' % \ (ColumnHelper.add(col_index, -1) + str(row_index - 1)) self.write_dimension.set('ref', dimension)
def get_next_column(self, current_col, end_col, begin_col): """ Получение следующей колонки :param current_col: текущая колонка :type current_col: str :param end_col: конечная колонка :type end_col: str :param begin_col: начальная колонка :type begin_col: str """ return ColumnHelper.add(current_col, ColumnHelper.difference(end_col, begin_col) + 1)
def get_next_column(self, current_col, end_col, begin_col): """ Получение следующей колонки :param current_col: текущая колонка :type current_col: str :param end_col: конечная колонка :type end_col: str :param begin_col: начальная колонка :type begin_col: str """ return ColumnHelper.add( current_col, ColumnHelper.difference(end_col, begin_col) + 1)
def _get_next_index(self, cell_coord, may_change, diff, is_column): """ Изменяем индекс у столбца(строки) ячейки(в зависимости от флага is_column) may_change - можно ли индекс изменять diff - на сколько нужно изменить индекс """ reg_exp = self.col_re if is_column else self.row_re index = reg_exp.search(cell_coord).group() if may_change: if is_column: index = ColumnHelper.add(index, diff) else: index = str(int(index) + diff) else: index = ''.join(['$', index]) return index
def flush( self, params, oriented=ISpreadsheetSection.LEFT_DOWN, used_formulas=None ): """ Вывод. Имеется два механизма вывода. Для использования старого не передавать direction :param params: параметры замены :type params: dict :param oriented: направление ориентации :type oriented: ISpreadsheetSection :param used_formulas: использованные формулы :type used_formulas: dict """ assert isinstance(params, dict) assert oriented in (Section.VERTICAL, Section.HORIZONTAL, Section.LEFT_DOWN, Section.RIGHT_UP, Section.RIGHT, Section.HIERARCHICAL) # Тут смещение курсора, копирование данных из листа и общих строк # Генерация новых данных и новых общих строк cursor = self.sheet_data.cursor begin_col, begin_row = self.begin end_col, end_row = self.sheet_data.get_cell_end(self.end) current_col, current_row = CalculateNextCursorXLSX().get_next_cursor(cursor, (begin_col, begin_row), (end_col, end_row), oriented, self) self.sheet_data.last_section.row = (current_col, current_row) self.sheet_data.last_section.column = (ColumnHelper.add(current_col, ColumnHelper.difference(end_col, begin_col)), current_row + end_row - begin_row) self.sheet_data.flush(self.begin, self.end, (current_col, current_row), params, used_formulas)
def flush(self, params, oriented=ISpreadsheetSection.LEFT_DOWN, used_formulas=None): """ Вывод. Имеется два механизма вывода. Для использования старого не передавать direction :param params: параметры замены :type params: dict :param oriented: направление ориентации :type oriented: ISpreadsheetSection :param used_formulas: использованные формулы :type used_formulas: dict """ assert isinstance(params, dict) assert oriented in (Section.VERTICAL, Section.HORIZONTAL, Section.LEFT_DOWN, Section.RIGHT_UP, Section.RIGHT, Section.HIERARCHICAL) # Тут смещение курсора, копирование данных из листа и общих строк # Генерация новых данных и новых общих строк cursor = self.sheet_data.cursor begin_col, begin_row = self.begin end_col, end_row = self.sheet_data.get_cell_end(self.end) current_col, current_row = CalculateNextCursorXLSX().get_next_cursor( cursor, (begin_col, begin_row), (end_col, end_row), oriented, self) self.sheet_data.last_section.row = (current_col, current_row) self.sheet_data.last_section.column = (ColumnHelper.add( current_col, ColumnHelper.difference( end_col, begin_col)), current_row + end_row - begin_row) self.sheet_data.flush(self.begin, self.end, (current_col, current_row), params, used_formulas)
def cell_dimensions(section, merge_cell, start_cell): """ Получение координаты ячейки после смещения из-за объединения ячеек :param section: начало секции :type section: 2-tuple :param merge_cell: начало объединенной ячейки :type merge_cell: 2-tuple :param start_cell: стартовая ячейка :type start_cell: 2-tuple """ section_begin_col, section_begin_row = section start_col, start_row = start_cell begin_col, begin_row = merge_cell new_begin_row = start_row + begin_row - section_begin_row new_begin_col = ColumnHelper.add(start_col, ColumnHelper.difference(begin_col, section_begin_col)) return new_begin_col + str(new_begin_row)
def set_section(self, begin, end, start_cell, params, used_formulas=None): u""" Вывод секции :param begin: начальная ячейка секции :type begin: 2-tuple :param end: конечная ячейка секции :type end: 2-tuple :param start_cell: стартовая ячейка (с которой начинается запись) :type start_cell: 2-tuple :param params: параметры отчета :type params: dict :param used_formulas: используемые формулы :type used_formulas: dict """ # TODO: разбить на методы range_rows, range_cols = self._range(begin, end) start_column, start_row = start_cell if used_formulas is None: used_formulas = {} for i, num_row, row in self._find_rows(range_rows): attrib_row = dict(row.items()) row_index = start_row + i attrib_row['r'] = str(row_index) # следующая строка создает линейное время для флаша от числа флашей row_el = self._create_or_get_output_row(row_index, attrib_row) for j, col, cell in self._find_cells(range_cols, num_row, row): attrib_cell = dict(cell.items()) col_index = ColumnHelper.add(start_column, j) attrib_cell['r'] = col_index + str(row_index) cell_el = self._create_or_get_output_cell( row_el, col_index + str(row_index), attrib_cell) # Перенос формул formula = self._get_tag_formula(cell) if formula is not None: formula_el = SubElement(cell_el, 'f') row_cursor_column, row_cursor_row = self.cursor.row column_cursor_column, column_cursor_row = self.cursor.column formula_el.text = Formula.get_instance( formula.text).get_next_formula(row_cursor_row, column_cursor_column) # Если есть формула, то значение является вычисляемым параметром и не сильно интересует continue value = self._get_tag_value(cell) if not value is None: value_el = SubElement(cell_el, 'v') if attrib_cell.get('t') in ('n', None): # number value_el.text = value.text elif attrib_cell.get( 't' ) == 's': # 't' = 's' - значит есть значения shared strings index_value = int(value.text) value_string = self.shared_table.get_value(index_value) who_found_params = self._get_values_by_re(value_string) is_int = False if who_found_params: for found_param in who_found_params: param_name = found_param[1:-1] param_value = params.get(param_name) if used_formulas: formula_id_list = used_formulas.get( param_name) assert (formula_id_list is None or isinstance( formula_id_list, (list, tuple)) ), ("used_formulas values must be " "lists or tuples") if formula_id_list is not None: for formula_id in formula_id_list: cell_string = ''.join( [col_index, str(row_index)]) self.formula_id_dict.setdefault( formula_id, []).append(cell_string) # Находим теги шаблонов, если есть таковые if param_name[ 0] == self.PREFIX_TAG and param_name[ -1] == self.PREFIX_TAG: param_value = self.tags.get( param_name[1:-1]) if isinstance( param_value, datetime ) and found_param == value_string: # В OpenXML хранится дата относительно 1900 года days = date_to_float(param_value) if days > 0: # Дата конвертируется в int, начиная с 31.12.1899 is_int = True cell_el.attrib[ 't'] = 'n' # type - number value_el.text = unicode(days) else: date_less_1900 = '%s.%s.%s' % ( param_value.date().day, param_value.date().month, param_value.date().year) # strftime(param_value, locale.nl_langinfo(locale.D_FMT)) - неработает для 1900 и ниже value_string = value_string.replace( found_param, unicode(date_less_1900)) # Добавил long, возможно нужно использовать # общего предка numbers.Number elif isinstance( param_value, (int, float, long, Decimal)) and found_param == value_string: # В первую очередь добавляем числовые значения is_int = True cell_el.attrib['t'] = 'n' # type - number value_el.text = unicode(param_value) elif isinstance(param_value, basestring): # Строковые параметры value_string = value_string.replace( found_param, unicode(param_value)) elif isinstance(param_value, FormulaWriteExcel): # Записываем формулу, которой не было в # шаблоне func_ = param_value.excel_function f_id = param_value.formula_id if f_id and func_: attrib_cell['t'] = 'e' # тип вычислимого по формуле поля cell_el.remove(value_el) # значения в ячейке с формулой нет formula_el = SubElement(cell_el, 'f') row_cursor_column, row_cursor_row = self.cursor.row column_cursor_column, column_cursor_row = self.cursor.column f_cells = self.formula_id_dict.get( f_id, []) if param_value.ranged and f_cells: formula_el.text = '%s(%s)' % ( func_, ':'.join( [f_cells[0], f_cells[-1]])) elif f_cells: # Группируем по 30 элементов cells_number = int( len(f_cells) / XLSX_GROUPING_COUNT) + 1 val_list = [] for x in range(cells_number): sub_args = (','.join(f_cells[ x * XLSX_GROUPING_COUNT:(x + 1) * XLSX_GROUPING_COUNT])) if sub_args: sub_val = '(%s)' % ( sub_args) val_list.append(sub_val) formula_el.text = '%s(%s)' % ( func_, ','.join(val_list)) self.formula_id_dict[f_id] = [] else: continue elif param_value: # любой объект, например список или # словарь. Вынесено для производительности value_string = value_string.replace( found_param, unicode(param_value)) else: # Не передано значение параметра value_string = value_string.replace( found_param, '') if not is_int: # Добавим данные в shared strings new_index = self.shared_table.get_new_index( value_string) value_el.text = new_index else: # Параметры в поле не найдены index = self.shared_table.get_new_index( value_string) value_el.text = index elif attrib_cell.get('t'): raise SheetDataException( "Unknown value '%s' for tag t" % attrib_cell.get('t'))
def set_section(self, begin, end, start_cell, params, used_formulas=None): u""" Вывод секции :param begin: начальная ячейка секции :type begin: 2-tuple :param end: конечная ячейка секции :type end: 2-tuple :param start_cell: стартовая ячейка (с которой начинается запись) :type start_cell: 2-tuple :param params: параметры отчета :type params: dict :param used_formulas: используемые формулы :type used_formulas: dict """ # TODO: разбить на методы range_rows, range_cols = self._range(begin, end) start_column, start_row = start_cell if used_formulas is None: used_formulas = {} for i, num_row, row in self._find_rows(range_rows): attrib_row = dict(row.items()) row_index = start_row + i attrib_row['r'] = str(row_index) # следующая строка создает линейное время для флаша от числа флашей row_el = self._create_or_get_output_row(row_index, attrib_row) for j, col, cell in self._find_cells(range_cols, num_row, row): attrib_cell = dict(cell.items()) col_index = ColumnHelper.add(start_column, j) attrib_cell['r'] = col_index + str(row_index) cell_el = self._create_or_get_output_cell(row_el, col_index + str(row_index), attrib_cell) # Перенос формул formula = self._get_tag_formula(cell) if formula is not None: formula_el = SubElement(cell_el, 'f') row_cursor_column, row_cursor_row = self.cursor.row column_cursor_column, column_cursor_row = self.cursor.column formula_el.text = Formula.get_instance(formula.text).get_next_formula(row_cursor_row, column_cursor_column) # Если есть формула, то значение является вычисляемым параметром и не сильно интересует continue value = self._get_tag_value(cell) if not value is None: value_el = SubElement(cell_el, 'v') if attrib_cell.get('t') in ('n', None): # number value_el.text = value.text elif attrib_cell.get('t') == 's': # 't' = 's' - значит есть значения shared strings index_value = int(value.text) value_string = self.shared_table.get_value(index_value) who_found_params = self._get_values_by_re(value_string) is_int = False if who_found_params: for found_param in who_found_params: param_name = found_param[1:-1] param_value = params.get(param_name) if used_formulas: formula_id_list = used_formulas.get(param_name) assert ( formula_id_list is None or isinstance( formula_id_list, (list, tuple)) ), ("used_formulas values must be " "lists or tuples") if formula_id_list is not None: for formula_id in formula_id_list: cell_string = ''.join([col_index, str(row_index)]) self.formula_id_dict.setdefault( formula_id, []).append(cell_string) # Находим теги шаблонов, если есть таковые if param_name[0] == self.PREFIX_TAG and param_name[-1] == self.PREFIX_TAG: param_value = self.tags.get(param_name[1:-1]) if isinstance(param_value, datetime) and found_param == value_string: # В OpenXML хранится дата относительно 1900 года days = date_to_float(param_value) if days > 0: # Дата конвертируется в int, начиная с 31.12.1899 is_int = True cell_el.attrib['t'] = 'n' # type - number value_el.text = unicode(days) else: date_less_1900 = '%s.%s.%s' % ( param_value.date().day, param_value.date().month, param_value.date().year ) # strftime(param_value, locale.nl_langinfo(locale.D_FMT)) - неработает для 1900 и ниже value_string = value_string.replace(found_param, unicode(date_less_1900)) # Добавил long, возможно нужно использовать # общего предка numbers.Number elif isinstance( param_value, (int, float, long, Decimal) ) and found_param == value_string: # В первую очередь добавляем числовые значения is_int = True cell_el.attrib['t'] = 'n' # type - number value_el.text = unicode(param_value) elif isinstance(param_value, basestring): # Строковые параметры value_string = value_string.replace(found_param, unicode(param_value)) elif isinstance(param_value, FormulaWriteExcel): # Записываем формулу, которой не было в # шаблоне func_ = param_value.excel_function f_id = param_value.formula_id if f_id and func_: attrib_cell['t'] = 'e' # тип вычислимого по формуле поля cell_el.remove(value_el) # значения в ячейке с формулой нет formula_el = SubElement(cell_el, 'f') row_cursor_column, row_cursor_row = self.cursor.row column_cursor_column, column_cursor_row = self.cursor.column f_cells = self.formula_id_dict.get( f_id, []) if param_value.ranged and f_cells: formula_el.text = '%s(%s)' % ( func_, ':'.join([f_cells[0], f_cells[-1]]) ) elif f_cells: # Группируем по 30 элементов cells_number = int( len(f_cells) / XLSX_GROUPING_COUNT ) + 1 val_list = [] for x in range(cells_number): sub_args = ( ','.join( f_cells[x * XLSX_GROUPING_COUNT:(x + 1) * XLSX_GROUPING_COUNT] ) ) if sub_args: sub_val = '(%s)' % ( sub_args ) val_list.append(sub_val) formula_el.text = '%s(%s)' % ( func_, ','.join(val_list) ) self.formula_id_dict[f_id] = [] else: continue elif param_value: # любой объект, например список или # словарь. Вынесено для производительности value_string = value_string.replace(found_param, unicode(param_value)) else: # Не передано значение параметра value_string = value_string.replace(found_param, '') if not is_int: # Добавим данные в shared strings new_index = self.shared_table.get_new_index(value_string) value_el.text = new_index else: # Параметры в поле не найдены index = self.shared_table.get_new_index(value_string) value_el.text = index elif attrib_cell.get('t'): raise SheetDataException("Unknown value '%s' for tag t" % attrib_cell.get('t'))