forked from sheetjs/docs.sheetjs.com
		
	
		
			
				
	
	
		
			137 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			137 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from base64 import b64encode, b64decode
 | 
						|
from contextlib import contextmanager
 | 
						|
from STPyV8 import JSContext, JSArray, JSObject
 | 
						|
from functools import wraps
 | 
						|
from os.path import splitext
 | 
						|
 | 
						|
def to_py(method):
 | 
						|
  # `convert` from STPyV8 tests/test_Wrapper.py
 | 
						|
  def convert(obj):
 | 
						|
    if isinstance(obj, JSArray):
 | 
						|
      return [convert(v) for v in obj]
 | 
						|
    if isinstance(obj, JSObject):
 | 
						|
      return dict([[str(k), convert(obj.__getattr__(str(k)))] for k in obj.__dir__()])
 | 
						|
    return obj
 | 
						|
 | 
						|
  @wraps(method)
 | 
						|
  def func(self, *args, **kwargs):
 | 
						|
    res = method(self, *args, **kwargs)
 | 
						|
    return convert(res)
 | 
						|
  return func
 | 
						|
 | 
						|
class SheetJSWorksheet:
 | 
						|
  ws = None
 | 
						|
  ctxt = None
 | 
						|
 | 
						|
  def __init__(self, ctxt, ws):
 | 
						|
    self.ctxt = ctxt
 | 
						|
    self.ws = ws
 | 
						|
 | 
						|
  def js(self): return self.ws
 | 
						|
 | 
						|
  @to_py
 | 
						|
  def get_rows(self):
 | 
						|
    return self.ctxt.eval("(ws => XLSX.utils.sheet_to_json(ws))")(self.ws)
 | 
						|
 | 
						|
class SheetJSWorkbook:
 | 
						|
  wb = None
 | 
						|
  ctxt = None
 | 
						|
 | 
						|
  def __init__(self, ctxt, wb):
 | 
						|
    self.ctxt = ctxt
 | 
						|
    self.wb = wb
 | 
						|
 | 
						|
  def js(self): return self.wb
 | 
						|
 | 
						|
  @to_py
 | 
						|
  def sheet_names(self):
 | 
						|
    return self.wb.SheetNames
 | 
						|
 | 
						|
  def get_sheet(self, name):
 | 
						|
    return SheetJSWorksheet(self.ctxt, self.wb.Sheets[name])
 | 
						|
 | 
						|
  def to_file(self, path, book_type=""):
 | 
						|
    b64ify = self.ctxt.eval("((wb, bT) => XLSX.write(wb, {type:'base64', bookType:bT}))")
 | 
						|
    if not book_type: book_type = splitext(path)[1][1:]
 | 
						|
    b64 = b64ify(self.wb, book_type)
 | 
						|
    raw = b64decode(b64)
 | 
						|
    with open(path, mode="wb") as f:
 | 
						|
      f.write(raw)
 | 
						|
 | 
						|
class SheetJSWrapper:
 | 
						|
  ctxt = None
 | 
						|
 | 
						|
  def __init__(self, ctx):
 | 
						|
    self.ctxt = ctx
 | 
						|
    with open("xlsx.full.min.js") as f: self.ctxt.eval(f.read())
 | 
						|
 | 
						|
  def version(self):
 | 
						|
    return self.ctxt.eval("XLSX.version")
 | 
						|
 | 
						|
  def read_binary(self, data):
 | 
						|
    read = self.ctxt.eval("(b64 => XLSX.read(b64, {type: 'base64', dense: true}))")
 | 
						|
    return SheetJSWorkbook(self.ctxt, read(b64encode(data)))
 | 
						|
 | 
						|
  def read_file(self, path):
 | 
						|
    with open(path, mode="rb") as f:
 | 
						|
      return self.read_binary(f.read())
 | 
						|
 | 
						|
  def sheet_from_json(self, json):
 | 
						|
    jsonify = self.ctxt.eval("(json => XLSX.utils.json_to_sheet(JSON.parse(json)) )")
 | 
						|
    return SheetJSWorksheet(self.ctxt, jsonify(json))
 | 
						|
 | 
						|
  def book_new(self):
 | 
						|
    booknew = self.ctxt.eval("XLSX.utils.book_new()")
 | 
						|
    return SheetJSWorkbook(self.ctxt, booknew)
 | 
						|
 | 
						|
  def book_append_sheet(self, book, sheet, wsname):
 | 
						|
    bas = self.ctxt.eval("((wb, ws, wsname) => XLSX.utils.book_append_sheet(wb, ws, wsname))")
 | 
						|
    bas(book.js(), sheet.js(), wsname)
 | 
						|
 | 
						|
  def book_from_json(self, json, wsname = "Sheet1"):
 | 
						|
    booknew = self.book_new()
 | 
						|
    sheet = self.sheet_from_json(json)
 | 
						|
    self.book_append_sheet(booknew, sheet, wsname)
 | 
						|
    return booknew
 | 
						|
 | 
						|
  def book_from_df(self, df):
 | 
						|
    # convert from dataframe to JSON string
 | 
						|
    json = df.to_json(orient="records")
 | 
						|
    return self.book_from_json(json)
 | 
						|
 | 
						|
@contextmanager
 | 
						|
def SheetJS():
 | 
						|
  """
 | 
						|
  SheetJS Library context manager
 | 
						|
 | 
						|
  Returns an instance of the SheetJSWrapper class
 | 
						|
 | 
						|
  Reading data from file to Pandas DataFrame:
 | 
						|
 | 
						|
  ```py
 | 
						|
  with SheetJS() as sheetjs:
 | 
						|
      # read data from file
 | 
						|
      wb = sheetjs.read_file(argv[1])
 | 
						|
 | 
						|
      # get first worksheet
 | 
						|
      first_ws_name = wb.sheet_names()[0]
 | 
						|
      ws = wb.get_sheet(wsname)
 | 
						|
 | 
						|
      # get data from first worksheet (list of dicts)
 | 
						|
      rows = ws.get_rows()
 | 
						|
 | 
						|
      # generate pandas DataFrame
 | 
						|
      df = pd.DataFrame.from_records(rows)
 | 
						|
  ```
 | 
						|
 | 
						|
  Writing data from Pandas DataFrame to file:
 | 
						|
 | 
						|
  ```py
 | 
						|
  with SheetJS() as sheetjs:
 | 
						|
    sheetjs.book_from_df(df).to_file(outf)
 | 
						|
  ```
 | 
						|
 | 
						|
  """
 | 
						|
  with JSContext() as ctxt:
 | 
						|
    yield SheetJSWrapper(ctxt)
 |