PK!Ütinydb_viewer/__init__.pyfrom flask import Flask app = Flask(__name__) from .main import TinyDB from .views import index from .api import create_table, edit_record, delete_record PK!r.Mtinydb_viewer/api.pyfrom flask import request, jsonify, Response from . import app from .config import config @app.route('/api/create', methods=['POST']) def create_table(): r = request.get_json() assert r['fileId'] == config['file_id'] config.update({ 'table': config['tinydb'].table(r['tableName']), 'handsontable': r['handsontable'] }) return Response(status=201) @app.route('/api/edit', methods=['POST']) def edit_record(): record = request.get_json() record_id = config['table'].update({ record['fieldName']: record['data'] }, config['query'].id == record['id']) record = config['table'].get(record_id) return jsonify({ 'id': record_id, 'record': record }), 201 @app.route('/api/delete/', methods=['DELETE']) def delete_record(record_id): record = config['table'].get(record_id) config['table'].remove(doc_ids=[record_id]) return jsonify({ 'id': record_id, 'record': record }), 303 PK!tinydb_viewer/config.pyconfig = { 'host': 'localhost', 'port': 9000, 'debug': False, 'tinydb': None, 'file_id': None, 'handsontable': dict() }PK!D_WWtinydb_viewer/main.pyimport tinydb import os from . import app from .table import ViewableTable from .config import config class TinyDB(tinydb.TinyDB): def __init__(self, db_path, server_kwargs=None, show_datetime=True, *args, **kwargs): """Modifies TinyDB by kwargs.setdefault('ensure_ascii', False) Arguments: db_path {str} -- Path to JSON database server_kwargs {dict} -- dict to pass to app.run() Keyword Arguments: parse_datetime {bool} -- Whether to not to try parsing datetime from string (default: {True}) dateutil_kwargs {dict} -- kwargs to pass to dateutil.parser.parse() (default: {dict()}) """ if server_kwargs is None: server_kwargs = dict() kwargs.setdefault('ensure_ascii', False) self.query = tinydb.Query() config.update({ 'tinydb': self, 'file_id': os.stat(db_path).st_ino, 'query': self.query, 'show_datetime': show_datetime, **server_kwargs }) self.table_class = ViewableTable super().__init__(db_path, *args, **kwargs) @classmethod def runserver(cls): app.run( host=config['host'], port=config['port'], debug=config['debug'] ) PK!#cctinydb_viewer/renderer.pyimport pyexcel import requests from .config import config class DataTable: def __init__(self, records, table_name, **kwargs): renderers = kwargs.pop('renderers', dict()) web_config = kwargs.pop('config', dict()) headers = list() for record in records: for k in record.keys(): if k not in headers: headers.append(k) columns = [] for header in headers: columnData = { 'data': header } if header in renderers.keys(): columnData['renderer'] = renderers.get(header) columns.append(columnData) web_config.update({ 'colHeaders': headers, 'rowHeaders': [r.doc_id for r in records], 'columns': columns, 'data': records }) self.web_config = web_config self.table_name = table_name self.kwargs = kwargs self.pyexcel_records = list() for record in records: self.pyexcel_records.append([record.doc_id] + record) def _repr_html_(self): try: height = self.kwargs.get('height', None) url = 'http://{}:{}'.format(config['host'], config['port']) r = requests.post('{}/api/create'.format(url), json={ 'tableName': self.table_name, 'handsontable': self.web_config, 'fileId': config['file_id'] }) r.raise_for_status() return ''.format( url, self.kwargs.get('width', 800), 'height={}'.format(height) if height else '' ) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError): return pyexcel.get_sheet(records=self.pyexcel_records).html PK! O:tinydb_viewer/static/viewer.jsvar container = document.getElementById('hotArea'); var actualConfig = { undo: true, modifyColWidth: function(width, col){ if(width > maxColWidth) return maxColWidth; }, contextMenu: [ // 'row_above', 'row_below', 'remove_row' ], afterChange: (changes, source)=>{ if([ 'edit', 'Autofill.fill', 'CopyPaste.paste', 'UndoRedo.redo', 'UndoRedo.undo' ].indexOf(source) !== -1){ for(let change of changes){ if(change[2] !== change[3]){ fetch('api/edit', { method: 'POST', headers: { "Content-Type": "application/json; charset=utf-8" }, body: JSON.stringify({ id: hot.getRowHeader(change[0]), fieldName: change[1], data: change[3], table: table }) }).then(r=>{ if(r.status === 201){ r.json() } else { throw 'Request failed.'; } }).then(rj=>{ alert(rj.record + 'edited'); }).catch(e=>{ alert(e); hot.setDataAtCell([ [change[0], change[1], change[2]] ]) }) } } } }, beforeRemoveRow: (index, amount, physicalRows)=>{ if(confirm('Are you sure you want ot remove ' + hot.getDataAtRow(index) + '?')){ fetch('api/delete/' + table + '/'+ index, { method: 'DELETE' }).then(r=>{ if(r.status === 303){ r.json(); } }).then(rj=>{ alert(rj.record + 'deleted'); }).catch(e=>console.error(e)); } else { return false; } } } config = Object.assign(actualConfig, config); var hot = new Handsontable(container, config); PK!R tinydb_viewer/table.pytry: from tinydb_constraint import ConstraintTable as Table except ImportError: from tinydb.database import Table from .renderer import DataTable class ViewableTable(Table): view_dict = None def all(self, **kwargs): records = super().all() self._viewer_init(records, **kwargs) return records def search(self, cond, **kwargs): records = super().search(cond) self._viewer_init(records, **kwargs) return records def _viewer_init(self, records, chunk_size=10, sort_func=None, viewer_func=None, **kwargs): if viewer_func is None: viewer_func = lambda x: DataTable(x, table_name=self.name, **kwargs) if sort_func: records = sorted(records, key=sort_func) self.view_dict = { 'func': viewer_func, 'records': records, 'i': 0, 'chuck_size': chunk_size } def view(self, page_number=None, start=None): """Choose a page number to view Keyword Arguments: page_number {int >= -1} -- Page number to view (default: {self.i}) start {int} -- Sequence of the record to start viewing (default: {None}) Returns: Viewer function object """ if self.view_dict: if page_number is None: page_number = self.view_dict['i'] elif page_number == -1: page_number = len(self.view_dict['records']) // self.view_dict['chuck_size'] self.view_dict['i'] = page_number if start is None: start = page_number * self.view_dict['chuck_size'] return self.view_dict['func'](self.view_dict['records'][start: start + self.view_dict['chuck_size']]) else: return 'Please search() first.' def next(self): """Shows the next page Returns: Viewer function object """ if self.view_dict: if len(self.view_dict['records']) < (self.view_dict['i'] + 1) * self.view_dict['chuck_size']: self.view_dict['i'] = 0 else: self.view_dict['i'] += 1 return self.view() else: return 'Please search() first.' def previous(self): """Show the previous page Returns: Viewer function object """ if self.view_dict: self.view_dict['i'] -= 1 if self.view_dict['i'] < 0: self.view_dict['i'] = len(self.view_dict['records']) // self.view_dict['chuck_size'] return self.view() else: return 'Please search() first.' def first(self): """Shows the first page Returns: Viewer function object """ return self.view(0) def last(self): """Shows the last page Returns: Viewer function object """ return self.view(-1) PK!-2#tinydb_viewer/templates/viewer.html {{ title }}
PK!GP~>>tinydb_viewer/util.pyimport unicodedata, re all_chars = (chr(i) for i in range(0x110000)) control_chars = ''.join(c for c in all_chars if unicodedata.category(c) in {'Cc'}) control_char_re = re.compile('[%s]' % re.escape(control_chars)) def remove_control_chars(s): return unicodedata.normalize("NFKD", control_char_re.sub('', s)) PK!e׸tinydb_viewer/views.pyfrom flask import render_template from . import app from .config import config @app.route('/') def index(): return render_template('viewer.html', config=config['handsontable']) PK! ::%tinydb_viewer-0.2.3.dist-info/LICENSEMIT License Copyright (c) 2018 Pacharapol Withayasakpunt Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!H WX#tinydb_viewer-0.2.3.dist-info/WHEEL A н#Z."jm)Afb~ڠO68oF04UhoAf f4=4h0k::wXPK!H|v<D8 &tinydb_viewer-0.2.3.dist-info/METADATAVmo6_qE,*N I4o R/`5--&T(qݦ݂-y#Ȅ+J`ؙ(1'UM; 4w.P0MW Sm2 sTh fF0o@ D!o id2uR؉.1ĜRU6ty=.TQbo {/STH W,sO(=W#1-.& !| ⶪ[",,XyWvTkL"Y$C8*ޠ\=7,{5 eVSOOfx2%YK6:J`Fn{?Q5xb1/޻cG^SDsm#ZghqTpAMDGZ9T.5TR8fzbt]a"\jd9j*mwޚ!<ε!=.,|?_8:d`s"s9f:m "YK=]Y-*DA1hS$ & yHI @FQ#8*B @6s E:4:Ӭ+Am&-YS&q!.G'< 8~_ ~|{9ikd2vY+`PQ,d[=4qBRJ̤k "qfmAڧ4YVڸϳmX:=njt{0G9>iDW ֔iSo=88AuRIK`qgu3K?jf_u/ P1P14K,9㷡;k z(Ί?nçNyUmA+{>Yr@j9Fm𓰩:_Daq2pԨqD螙,Vf#Vݻ|5FO55RGώ5D||J$x[ kG?_wc^oBmJ2bLE.hk9{:_*E5 Si^;_?,/eiTPK!Hg$tinydb_viewer-0.2.3.dist-info/RECORDɒ@< p"\*dBmx 'l/}.cN##> ,rpO>4bs=;<Κm8w!w;bF…QQ`YmUp勥 Y[8g.P۵8,%aY%zXl.=!)TVլeYlcӧ% NwI2By)0[S.>ez[Me65.)^>ˈOנ |ӆ=C>C4E2dQ0Ǧخ(4s4fEb "xe[Tɦ׎gi; ,dɵJ2TsRη,A<Ǝ_RbDpwetuOnWu Z cgTx5]-o1Ϡ<1f䨁hW}\+6zդ}7&D^xocټYk$9'I{䔷ڗ\U&q;hs=VQwޱht8~@6AX+6ق9Mp8 qI-8x$˶>,CVąaꮩݸo曕=ñD .ztAuU?}ύ Ѓ\ySQ9ՂT%lHįXJ{}nW*8jG]wʎį?PK!Ütinydb_viewer/__init__.pyPK!r.Mtinydb_viewer/api.pyPK!tinydb_viewer/config.pyPK!D_WWtinydb_viewer/main.pyPK!#ccD tinydb_viewer/renderer.pyPK! O:tinydb_viewer/static/viewer.jsPK!R tinydb_viewer/table.pyPK!-2#'tinydb_viewer/templates/viewer.htmlPK!GP~>>+tinydb_viewer/util.pyPK!e׸R-tinydb_viewer/views.pyPK! ::%>.tinydb_viewer-0.2.3.dist-info/LICENSEPK!H WX#2tinydb_viewer-0.2.3.dist-info/WHEELPK!H|v<D8 &S3tinydb_viewer-0.2.3.dist-info/METADATAPK!Hg$7tinydb_viewer-0.2.3.dist-info/RECORDPK :