PK!W]JJpyexcel_export/__init__.pyfrom .app import ExcelLoader from .defaults import Meta def get_stylesheet(in_file=None, **flags): """ :param in_file: supported file formats are *.xlsx, *.json and *.pyexcel.json :param flags: :return: """ if in_file: loader = ExcelLoader(in_file, **flags) return loader.meta else: return Meta(**flags) def save_data(out_file, data, stylesheet=None, retain_styles=False, **flags): """ :param out_file: supported file formats are *.xlsx, *.json and *.pyexcel.json :param data: :param stylesheet: :param retain_styles: whether you want to retain the overwritten worksheet's formatting :param flags :return: """ loader = ExcelLoader(**flags) loader.meta = stylesheet loader.data = data loader.save(out_file, retain_styles=retain_styles) PK!/pyexcel_export/app.pyimport pyexcel from collections import OrderedDict from datetime import datetime import os import json import copy from .serialize import RowExport, PyexcelExportEncoder, MyEncoder from .defaults import Meta from .formatter import ExcelFormatter class ExcelLoader: def __init__(self, in_file: str=None, data=None, **flags): if in_file: self.in_file = in_file self.meta = Meta(**flags) in_base, in_format = os.path.splitext(in_file) if in_format == '.xlsx': self.data = self._load_pyexcel_excel() elif in_format == '.json': if os.path.splitext(in_base)[1] == '.pyexcel': self.data = self._load_pyexcel_json() else: self.data = self._load_json() else: raise ValueError('Unsupported file format, {}.'.format(in_format)) elif data: self.meta = Meta(**flags) else: raise ValueError("Either in_file or data must be supplied.") def _load_pyexcel_excel(self): updated_data = pyexcel.get_book_dict(file_name=self.in_file) self.meta['_styles'] = ExcelFormatter(self.in_file).data return self._set_updated_data(updated_data) def _load_pyexcel_json(self): with open(self.in_file) as f: data = json.load(f, object_pairs_hook=OrderedDict) for sheet_name, sheet_data in data.items(): for j, row in enumerate(sheet_data): for i, cell in enumerate(row): data[sheet_name][j][i] = json.loads(cell) return self._set_updated_data(data) def _load_json(self): with open(self.in_file) as f: data = json.load(f, object_pairs_hook=OrderedDict) return self._set_updated_data(data) def _set_updated_data(self, updated_data): data = OrderedDict() if '_meta' in updated_data.keys(): for row in updated_data['_meta']: if not row or not row[0]: break if len(row) < 2: updated_meta_value = None else: updated_meta_value = row[1] self.meta[row[0]] = updated_meta_value try: self.meta.move_to_end('modified', last=False) self.meta.move_to_end('created', last=False) except KeyError as e: print(e) updated_data.pop('_meta') for k, v in updated_data.items(): data[k] = v return data @property def formatted_object(self): formatted_object = OrderedDict( _meta=self.meta.matrix ) for sheet_name, sheet_data in self.data.items(): formatted_sheet_object = [] for row in sheet_data: formatted_sheet_object.append(RowExport(row)) formatted_object[sheet_name] = formatted_sheet_object return formatted_object def save(self, out_file: str, retain_meta=True, out_format=None, retain_styles=True): self.meta['modified'] = datetime.fromtimestamp(datetime.now().timestamp()).isoformat() self.meta.move_to_end('modified', last=False) if 'created' in self.meta.keys(): self.meta.move_to_end('created', last=False) if out_format is None: out_base, out_format = os.path.splitext(out_file) else: out_base = os.path.splitext(out_file)[0] save_data = copy.deepcopy(self.data) if out_format == '.json' or retain_meta: save_data['_meta'] = self.meta.matrix if not retain_styles: for i, row in enumerate(save_data['_meta']): if row[0] == '_styles': save_data['_meta'].pop(i) break save_data.move_to_end('_meta', last=False) else: if '_meta' in save_data.keys(): save_data.pop('_meta') to_remove = [] for sheet_name, sheet_matrix in save_data.items(): if sheet_name == '_meta' or not sheet_name.startswith('_'): for i, row in enumerate(sheet_matrix): if out_format == '.json': save_data[sheet_name][i] = RowExport(row) else: to_remove.append(sheet_name) for sheet_name in to_remove: save_data.pop(sheet_name) if out_format == '.xlsx': self._save_openpyxl(out_file=out_file, out_data=save_data) elif out_format == '.json': if os.path.splitext(out_base)[1] == '.pyexcel': self._save_pyexcel_json(out_file=out_file, out_data=save_data) else: self._save_json(out_file=out_file, out_data=save_data) else: raise ValueError('Unsupported file format, {}.'.format(out_file)) def _save_openpyxl(self, out_file: str, out_data: OrderedDict): formatter = ExcelFormatter(out_file) if os.path.exists(out_file): self.meta['_styles'] = formatter.data formatter.save(out_data, out_file, meta=self.meta) @staticmethod def _save_pyexcel_json(out_file: str, out_data: OrderedDict): with open(out_file, 'w') as f: export_string = json.dumps(out_data, cls=PyexcelExportEncoder, indent=2, ensure_ascii=False) f.write(export_string) @staticmethod def _save_json(out_file: str, out_data: OrderedDict): with open(out_file, 'w') as f: export_string = json.dumps(out_data, cls=MyEncoder, indent=2, ensure_ascii=False) f.write(export_string) PK!k!((pyexcel_export/defaults.pyfrom datetime import datetime from collections import OrderedDict from io import BytesIO import base64 import json class Meta(OrderedDict): def __init__(self, **kwargs): default = OrderedDict([ ('created', datetime.fromtimestamp(datetime.now().timestamp()).isoformat()), ('has_header', True), ('freeze_header', True), ('col_width_fit_param_keys', True), ('col_width_fit_ids', True) ]) default.update(**kwargs) super().__init__(**default) @property def excel_view(self): result = OrderedDict(self) for k, v in self.items(): if type(v) not in (int, float, str): result[k] = {str(type(v)): v} return result @property def view(self): result = OrderedDict(self) for k, v in self.items(): if type(v) not in (int, float, str, bool): result[k] = {str(type(v)): v} return result @property def matrix(self): return list(self.view.items()) def __setitem__(self, key, item): if isinstance(item, dict) and len(item) == 1: k, v = list(item.items())[0] if k == "": item = v elif k == "": item = BytesIO(base64.b64decode(v)) super().__setitem__(key, item) def __repr__(self): output = OrderedDict() for k, v in self.items(): output[k] = repr(v) return json.dumps(output, indent=2) PK!洝pyexcel_export/dir.py""" Defines ROOT as project_name/project_name/. Useful when installing using pip/setup.py. """ import os import inspect MODULE_ROOT = os.path.abspath(os.path.dirname(inspect.getframeinfo(inspect.currentframe()).filename)) TRUE_ROOT = os.path.dirname(MODULE_ROOT) def module_path(filename): return os.path.join(MODULE_ROOT, filename) def root_path(filename): return os.path.join(TRUE_ROOT, filename) PK!pyexcel_export/formatter.pyimport openpyxl from openpyxl.utils import get_column_letter from openpyxl.worksheet.copier import WorksheetCopy import os import logging import json from io import BytesIO from collections import OrderedDict from .serialize import MyEncoder debug_logger = logging.getLogger('debug') class ExcelFormatter: def __init__(self, template_file: str=None): if template_file and os.path.exists(template_file): self.styled_wb = openpyxl.load_workbook(template_file) self.to_stylesheets(self.styled_wb) else: self.styled_wb = openpyxl.Workbook() self.styled_wb.active.title = '_template' @property def data(self): output = BytesIO() self.styled_wb.save(output) return output @data.setter def data(self, _styles): self.styled_wb = openpyxl.load_workbook(_styles) def save(self, raw_data, out_file, meta=None, retain_meta=True): if '_styles' in meta.keys(): self.data = meta['_styles'] meta['_styles'] = self.data if os.path.exists(out_file): wb = openpyxl.load_workbook(out_file) self.to_stylesheets(wb) self.append_styled_sheets(wb) original_sheet_names = [] extraneous_sheet_names = [] else: wb = self.styled_wb original_sheet_names = wb.sheetnames extraneous_sheet_names = [] extraneous_sheet_names.append('_template') inserted_sheets = [] if not retain_meta: extraneous_sheet_names.append('_meta') else: if '_meta' in original_sheet_names: wb.remove(wb['_meta']) self.create_styled_sheet(wb, '_meta', 0) meta_matrix = [] for k, v in meta.excel_view.items(): if not k.startswith('_'): if isinstance(v, (dict, OrderedDict)): v = json.dumps(v, cls=MyEncoder) meta_matrix.append([k, v]) self.fill_matrix(wb['_meta'], meta_matrix, rules=meta) if '_hidden' in meta.keys(): starting_index = len(meta_matrix) + 1 for k, v in meta['_hidden'].items(): wb['_meta'].append() table_matrix = [['# ' + k]] table_matrix.extend(v) self.fill_matrix(wb['_meta'], table_matrix, starting_index, rules=meta) starting_index += len(table_matrix) + 1 if '_meta' in raw_data.keys(): raw_data.pop('_meta') for sheet_name, cell_matrix in raw_data.items(): if sheet_name not in original_sheet_names: self.create_styled_sheet(wb, sheet_name) inserted_sheets.append(sheet_name) else: self.fill_matrix(wb[sheet_name], cell_matrix, rules=meta) ws = wb[sheet_name] for row_num, row in enumerate(cell_matrix): for col_num, value in enumerate(row): if isinstance(value, (dict, OrderedDict)): value = json.dumps(value, cls=MyEncoder) ws.cell(column=(col_num + 1), row=(row_num + 1), value=value) for sheet_name in extraneous_sheet_names: if sheet_name in wb.sheetnames: wb.remove(wb[sheet_name]) for sheet_name in wb.sheetnames: if sheet_name.startswith('_') and sheet_name != '_meta': wb.remove(wb[sheet_name]) wb.save(out_file) @staticmethod def create_styled_sheet(wb, sheet_name, pos: int=None): wb.create_sheet(sheet_name, pos) if '_template' in wb.sheetnames and sheet_name != '_template': WorksheetCopy(wb['_template'], wb[sheet_name]).copy_worksheet() # wb[sheet_name].copy_worksheet(wb['_template']) def append_styled_sheets(self, wb): if '_template' not in wb.sheetnames: wb.create_sheet('_template') if '_template' in self.styled_wb.sheetnames: WorksheetCopy(self.styled_wb['_template'], wb['_template']).copy_worksheet() # wb['_template'].copy_worksheet(wb['_template']) return wb @staticmethod def to_stylesheets(wb): for ws in wb: for row in ws: for cell in row: cell.value = None return wb @staticmethod def fill_matrix(ws, cell_matrix, start_row=0, rules=None): for row_num, row in enumerate(cell_matrix): for col_num, value in enumerate(row): if isinstance(value, (dict, OrderedDict)): value = json.dumps(value, cls=MyEncoder) ws.cell(column=(col_num + 1), row=(row_num + start_row + 1), value=value) if rules is not None: if rules.get('has_header', False) and rules.get('freeze_header', False): if ws.title != '_meta': ws.freeze_panes = 'A2' if rules.get('col_width_fit_param_keys', False): width = max([len(str(cell.value)) for cell in next(ws.iter_cols())]) ws.column_dimensions['A'].width = width + 2 if rules.get('col_width_fit_ids', False): for i, header_cell in enumerate(next(ws.iter_rows())): header_item = header_cell.value if header_item and header_item.endswith('id'): col_letter = get_column_letter(i + 1) width = max([len(str(cell.value)) for cell in list(ws.iter_cols())[i]]) ws.column_dimensions[col_letter].width = width + 2 PK!spyexcel_export/serialize.pyimport uuid import json import base64 from io import BytesIO class RowExport(object): def __init__(self, raw_row): self.value = [] for raw_cell in raw_row: self.value.append(json.dumps(raw_cell, ensure_ascii=False, cls=MyEncoder)) def __repr__(self): if not isinstance(self.value, list): return repr(self.value) else: # Sort the representation of any dicts in the list. reps = ('{{{}}}'.format(', '.join( ('{!r}:{}'.format(k, v) for k, v in sorted(v.items())) )) if isinstance(v, dict) else repr(v) for v in self.value) return '[' + ', '.join(reps) + ']' @property def data(self): raw_row = [] for cell in self.value: raw_row.append(json.loads(cell)) return raw_row class PyexcelExportEncoder(json.JSONEncoder): def __init__(self, *args, **kwargs): super(PyexcelExportEncoder, self).__init__(*args, **kwargs) self.kwargs = dict(kwargs) del self.kwargs['indent'] self._replacement_map = {} def default(self, o): if isinstance(o, RowExport): key = uuid.uuid4().hex self._replacement_map[key] = json.dumps(o.value, **self.kwargs) return "@@%s@@" % (key,) elif isinstance(o, BytesIO): return base64.b64encode(o.getvalue()).decode() else: return super(PyexcelExportEncoder, self).default(o) def encode(self, o): result = super(PyexcelExportEncoder, self).encode(o) for k, v in self._replacement_map.items(): result = result.replace('"@@%s@@"' % (k,), v) return result class MyEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, RowExport): return o.data elif isinstance(o, BytesIO): return base64.b64encode(o.getvalue()).decode() return json.JSONEncoder.default(self, o) PK!H_zTT$pyexcel_export-0.1.0.dist-info/WHEEL A н#Z;/"d&F[xzw@Zpy3Fv]n0H*J>mlcAPK!H-'pyexcel_export-0.1.0.dist-info/METADATAVMo6Wp$-KW,Ҥ@~bph$j$[) a ;]vi˶T #)[^R|×zD$ %r-xc4'SDU2Ma[e{Yc{hAAc4bϠXf#IP6ɑϢ"}M3)$6- V{z9 EѦYAݥ-d (֛s\5:}UM"ڼtiZ?r^Md߼܀8JuvZ鸅#H<4ʙC.s臠0@i=U:^2]eS1-_6b9utZ븭,= x{n1pw?~e9+ϓq+N%{ubis)VE-%ɝ% __W?'ҐaAD%" 4ͷJq7~F-x?J^יּPK!Hȼ%pyexcel_export-0.1.0.dist-info/RECORDɒ@{ K9 ظ#z! ),ņ/8@Ow}cgUbQ6>[[Dz\iL2/l8S(p,~_0s0=nL#4'eGa=r:ނN )ߵd+$'T 'ӈ+J:ɗV2gZgsdܙlfcewʦv8"N;-y%oNn|e3:irm3ET)l]P~7r\#y1Cu uGa?Gpf_! q o&/LDyy>B|izpDC8tv=׳j(G`F/oXo(ZJe1h3 ̻@5Y$p* 2מqPK!W]JJpyexcel_export/__init__.pyPK!/pyexcel_export/app.pyPK!k!((wpyexcel_export/defaults.pyPK!洝 pyexcel_export/dir.pyPK!"pyexcel_export/formatter.pyPK!s9pyexcel_export/serialize.pyPK!H_zTT$Apyexcel_export-0.1.0.dist-info/WHEELPK!H-'Bpyexcel_export-0.1.0.dist-info/METADATAPK!Hȼ%Hpyexcel_export-0.1.0.dist-info/RECORDPK J