#coding: utf-8
import xlrd
from uuid import uuid4
from xlrd.sheet import Sheet
from xlutils.filter import XLWTWriter
from simple_report.core.exception import SheetNotFoundException, SectionNotFoundException
from simple_report.xls.section import Section
from simple_report.xls.cursor import CursorXLS
from simple_report.converter.abstract import FileConverter
[docs]class WorkbookSheet():
# спец. символ конца строки
END_STRING = '\n'
def __init__(self, sheet, writer):
assert isinstance(sheet, Sheet), 'sheet must be xlrd.sheet.Sheet instance'
self.sheet = sheet
self.writer = writer
self.cursor = CursorXLS()
self.sections = {}
self.formula_id_dict = {}
[docs] def get_section(self, name):
"""
Получение секции по имени
:param name: имя секции
:result: секция
"""
assert name, 'Section name is empty'
if name not in self.sections:
begin = end = None
begin_section_text = u''.join(['+', name])
end_section_text = u''.join(['-', name])
# Получаем все примечания.
# sheet.cell_note_map представляет собой словарь
# { (colx, coly): note_instance }
notes = self.sheet.cell_note_map
for (note_coord, note) in notes.items():
# При составлении шаблона, разработчик может после имени секции вставить символ конца
# строки. В этом случае, данная секция не будет найдена. Поэтому необходимо данный
# символ обрезать.
note_text = note.text[:-1] if note.text[-1] == WorkbookSheet.END_STRING else note.text
if note_text == begin_section_text:
begin = note_coord
elif note_text == end_section_text:
end = note_coord
elif note_text in u''.join([begin_section_text, end_section_text]):
# Является ли данная ячейка смерженной.
row_coord, column_coord = note_coord
for merged_cell in self.sheet.merged_cells:
# Смерженная ячейка представленна в виде списка из 4 элементов
# (clo, rlo) - координаты левой верхней ячейки данной смерженной ячейки
# chi - количество колонок в смерженной ячейке
# rli - количество строк в смерженной ячейке
rlo, rhi, clo, chi = merged_cell
if column_coord == clo and row_coord == rlo:
# Сохраняем начало и конец смерженной ячейки
begin = (clo, rlo)
end = (chi - 1, rhi - 1)
break
if not (begin and end):
begin = end = note_coord
if not (begin and end):
raise SectionNotFoundException('Section named %s has not been found' % name)
self.sections[name] = Section(self, name, begin, end, self.writer)
return self.sections[name]
[docs] def get_sections(self):
"""
Получение всех секций
:result: словарь секций
"""
return self.sections
[docs] def get_name(self):
"""
Получение названия листа
:result: название листа
"""
return self.sheet.name
[docs]class Workbook(object):
def __init__(self, ffile, *args, **kwargs):
"""
"""
self.workbook = xlrd.open_workbook(ffile.file, formatting_info=True)
self.xlwt_writer = XLWTWriter()
self.xlwt_writer.start()
self.xlwt_writer.workbook(self.workbook, '%s.xls'%uuid4())
self.sheets = self._sheet_list()
if self.sheets:
pass
# self.init_active_sheet()
else:
raise SheetNotFoundException('Sheets not found')
for k, v in kwargs.items():
setattr(self, k, v)
[docs] def init_active_sheet(self):
"""
инициализация активного листа
:result: None
"""
self._active_sheet = self.sheets[0]
self.xlwt_writer.sheet(self._active_sheet.sheet, self._active_sheet.sheet.name)
[docs] def get_section(self, name):
"""
Получение секции по имени
:param name: имя секции
:result: секция
"""
if not hasattr(self, '_active_sheet'):
self._active_sheet = self.sheets[0]
self.xlwt_writer.sheet(self._active_sheet.sheet, self._active_sheet.sheet.name)
return self._active_sheet.get_section(name)
[docs] def get_sections(self):
"""
Получение всех секций
:result: словарь с секциями
"""
workbook_sections = {}
for sheet in self.sheets:
workbook_sections.update(sheet.get_sections())
return workbook_sections
@property
def active_sheet(self):
return self._active_sheet
@active_sheet.setter
[docs] def active_sheet(self, value):
assert isinstance(value, int)
try:
self._active_sheet = self.sheets[value]
except IndexError:
raise SheetNotFoundException('Sheet not found')
try:
self.xlwt_writer.sheet(self._active_sheet.sheet, self.get_sheet_name())
# Проставляем номер листа в выходном документе.
# Для этого берём номер соответствующего узла из шаблона и прибавляем 1, т.к.
# нумерация с нуля.
self.xlwt_writer.wtsheet.start_page_number = self.xlwt_writer.rdsheet.number + 1
# Настраиваем writer для работы с новым листом.
self.configure_writer()
except ValueError:
return
def _sheet_list(self):
all_sheets = self.workbook._sheet_list
sheet_list = []
for sheet in all_sheets:
sheet_list.append(WorkbookSheet(sheet, self.xlwt_writer))
return sheet_list
[docs] def get_sheet_name(self):
return self.active_sheet.get_name()
[docs] def build(self, dest_file):
"""
Сборка книги
:param dest_file: выходной путь
:result: None
"""
self.configure_writer()
dest_file_name = dest_file.file
self.xlwt_writer.finish()
# Получаем формат файла
dest_file_format = dest_file_name.split('.')[-1]
# Если не xls, то проставляем явно.
if dest_file_format != FileConverter.XLS:
dest_file_name = '%s.%s' % (dest_file_name, FileConverter.XLS)
# self.xlwt_writer.output имеет вид
# [('выходной файл1', Workbook1), ('выходной файл2', Workbook2), ... ]
# Для данного Workbook выбираем первый кортеж
ouput_file, workbook = self.xlwt_writer.output[0]
workbook.save(dest_file_name)
[docs] def write_sheet_count(self):
"""
Подсчитываем количество листов в которых есть секции
(другими словами в них будет производиться запись)
"""
# Берём листы из шаблона
read_sheets = self.xlwt_writer.rdbook._sheet_list
return len([read_sheet for read_sheet in read_sheets if read_sheet.cell_note_map])