Mercurial > hg > ooxml
view excel.py @ 74:7827e686be75 default tip
refactoring again...
author | Henry S. Thompson <ht@markup.co.uk> |
---|---|
date | Wed, 05 Jul 2017 18:26:27 +0100 |
parents | 4bd5de7ac247 |
children |
line wrap: on
line source
#!/usr/bin/python3 '''Class model for analysis of Excel spreadsheets''' from jsonweb.encode import to_object, dumper from jsonweb.decode import from_object, loader import json import eDecoder import re try: string_types=basestring except NameError: string_types=str @from_object() @to_object(exclude_nulls=True) class Book(object): def __init__(self,source,sheets=[],formats=[]): assert(isinstance(source,string_types)) self.source=source sheets=list(sheets) assert(all(isinstance(s,Sheet) for s in sheets)) self.sheets=sheets formats=list(formats) self.formats=formats def addSheet(self,sheet): assert(isinstance(sheet,Sheet)) self.sheets.append(sheet) @from_object() @to_object(exclude_nulls=True,suppress=["book"]) class Sheet(object): def __init__(self,name,book=None,tables=[],docs=[],misc=[]): assert(isinstance(name,string_types)) self.name=name tables=list(tables) assert(all(isinstance(s,Table) for s in tables)) self.tables=tables docs=list(docs) assert(all(isinstance(s,Region) for s in docs)) self.docs=docs misc=list(misc) assert(all(isinstance(s,Region) for s in misc)) self.misc=misc if book is not None: assert(isinstance(book,Book)) book.addSheet(self) self.book=book def addTable(self,table): assert(isinstance(table,Region)) self.tables.append(table) @from_object() @to_object(exclude_nulls=True,suppress=["parent"]) class Region(object): def __init__(self,name,bbox,parent=None): assert(isinstance(name,string_types)) self.name=name assert(isinstance(bbox,string_types)) self.bbox=Range(bbox) if parent is not None: assert(isinstance(parent,(Region,Sheet))) self.parent=parent if parent is not None: parent.addRegion(self) class CompoundRegion(Region): def __init__(self,name,bbox,content,parent=None): Region.__init__(self,name,bbox,parent) content=list(content) assert(all(isinstance(s,Region) for s in content)) self.content=content def addRegion(self,content): assert(isinstance(content,Region)) self.content.append(content) class SimpleRegion(Region): def __init__(self,name,bbox,hasNulls=False,parent=None): Region.__init__(self,name,bbox,parent) self.hasNulls=hasNulls cell=r'(\$?)([A-Z]+)(\$?)([1-9][0-9]*)' RANGE=re.compile(cell+(r'(:%s)?'%cell)) C=0 # Column R=1 # Row F=0 # Fixed ($) V=1 # Value @to_object() class Range(object): def __init__(self,estr): m=RANGE.match(estr) if m is None: raise ValueError("string %s doesn't represent a range"%estr) rgrps=m.groups() self.tl=((rgrps[0]=='$',rgrps[1]),((rgrps[2]=='$',int(rgrps[3])))) if rgrps[4] is None: self.br=None self.dim=0 self.vertical=None else: self.br=((rgrps[5]=='$',rgrps[6]),((rgrps[7]=='$',int(rgrps[8])))) if self.tl[C][V]==self.br[C][V]: self.dim=1 self.vertical=True elif self.tl[R][V]==self.br[R][V]: self.dim=1 self.vertical=False else: self.dim=2 self.vertical=None def __str__(self): tls=_cellStr(self.tl[C])+_cellStr(self.tl[R]) if self.br is None: return tls else: return '%s:%s'%(tls,_cellStr(self.br[C])+_cellStr(self.br[R])) def _cellStr(dvp): (dollar,val)=dvp return '%s%s'%('$' if dollar else '',str(val)) @from_object() @to_object(exclude_nulls=True,suppress=["sheet"]) class Table(CompoundRegion): def __init__(self,name,bbox,data,shape='mixed',sheet=None,labels=[]): data=list(data) if sheet is not None: assert(isinstance(sheet,Sheet)) CompoundRegion.__init__(self,name,bbox,data,sheet) assert(shape in ('columns','rows','mixed')) self.shape=shape # if columns or rows, that's what correspond to DB columns if labels is not None: labels=list(labels) assert(all(isinstance(s,Label) for s in labels)) for l in labels: self.addRegion(l) self.labels=labels self.data=data def addLabel(self,label): assert(isinstance(label,Label)) self.labels.append(label) Region.addRegion(self,label) def addData(self,data): assert(isinstance(data,Data)) self.data.append(data) Region.addRegion(self.data) def addRegion(self,region): assert(isinstance(region,(Label,Data))) if isinstance(region,Label): self.addLabel(region) else: self.addData(region) class Label(Region): '''Abstract base class, see {Compound,Simple}Label''' pass @from_object() @to_object(exclude_nulls=True,suppress=["parent"]) class CompoundLabel(Label,CompoundRegion): def __init__(self,name,bbox,content,parent=None): assert(all(isinstance(s,Label) for s in content)) CompoundRegion.__init__(self,name,bbox,content,parent) @from_object() @to_object(exclude_nulls=True,suppress=["parent"]) class SimpleLabel(Label,SimpleRegion): def __init__(self,name,bbox,type='string',parent=None): SimpleRegion.__init__(self,name,bbox,parent) assert(isinstance(type,string_types)) self.type=type def lt(filename): with open(filename,'r') as js: return loader(js.read(),cls=eDecoder.eDecoder)