def requires(*package_names): """Inform framework of the packages required for rendering >>> request = zoom.request.Request(dict(PATH_INFO='/')) >>> zoom.system.site = zoom.site.Site(request) >>> requires('c3') >>> libs = zoom.component.composition.parts.parts['libs'] >>> list(libs)[0] 'https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js' >>> try: ... requires('d4') ... except Exception as e: ... 'Missing required' in str(e) and 'raised!' 'raised!' """ parts = zoom.Component() registered_packages = get_registered_packages() for name in package_names: package = registered_packages.get(name) if package: parts += zoom.Component(**package) else: missing = set(package_names) - set(registered_packages) raise Exception('Missing required packages: {}'.format(missing)) zoom.component.composition.parts += parts
def side_menu(*items): css = """ .side-nav ul li { height: 2em; line-height: 2em; padding: 0 0.5em; list-style-type: none; } .side-nav ul .active { background: #ddd; } .content ul { margin-left: 0; } """ result = [] for item in items: item_id = zoom.utils.id_for(item) route = zoom.system.request.route attr = route[1:][:1] == [item_id] and {'class': 'active'} or {} result.append( h.tag( 'li', h.tag('a', item, href=zoom.helpers.url_for_page(item_id), id=item_id), **attr)) return zoom.Component( h.tag('ul', ''.join(result), classed='side-nav'), css=css, ) if result else ''
def environment(self): return page( zoom.Component( h.h2('Python'), zoom.tags.table([ ('Version', sys.version), ('PYTHONPATH', '<br>'.join(sys.path)), ('PATH', '<br>'.join(os.environ.get('PATH').split(':'))) ]), h.h2('Operating System'), zoom.tags.table([ ('Name', os.name), ('PYTHONPATH', '<br>'.join(sys.path)), ('PATH', '<br>'.join(os.environ.get('PATH').split(':'))) ]), h.h2('Platform'), zoom.tags.table([ ('Node', platform.node()), ('System', platform.system()), ('Machine', platform.machine()), ('Archtitecture', ' '.join(platform.architecture())) ]), h.h2('Variables'), zoom.tags.table( list(os.environ.items()) ), css = """ .content table { width: 100%; } .content table td { vertical-align: top; } """ ), title='Environment' )
def environment(self): return page(zoom.Component( h.h2('Zoom'), zoom.html.table([ ('Version', zoom.__version__ + ' Community Edition'), ('Installed Path', zoom.tools.zoompath()), ]), h.h2('Python'), zoom.html.table([ ('sys.version', sys.version), ('sys.path', '<br>'.join(sys.path)), ]), h.h2('Operating System'), zoom.html.table([ ('Name', os.name), ('PATH', '<br>'.join(os.environ.get('PATH').split(':'))) ]), h.h2('Platform'), zoom.html.table([('Node', platform.node()), ('System', platform.system()), ('Machine', platform.machine()), ('Archtitecture', ' '.join(platform.architecture()))]), h.h2('Variables'), zoom.html.table(list(os.environ.items())), css=""" .content table { width: 100%; } .content table td { vertical-align: top; width: 70%; } .content table td:first-child { width: 25%; } """), title='Environment')
def index(self): engine = zoom.system.site.config.get('database', 'engine') if engine == 'mysql': db = zoom.system.site.db sections = [ ('Settings', [ ('Connection', str(zoom.tools.websafe(zoom.system.site.db))), ('Isolation Level', get_isolation_level(zoom.system.site.db)) ]), ('Process List', zoom.system.site.db('show processlist')), ('Status', zoom.system.site.db('show status')), ] if db('show slave hosts'): sections.extend([ ('Replication Hosts', db('show slave hosts')), ('Replication Status', db('show slave status')) ]) content = zoom.Component( *((h.h2(title), h.table(code)) for title, code in sections), css=""" .content table { width: 100%; } .content table td {width: 50%; } """ ) else: content = 'not available for {} database engine'.format(engine) return zoom.page(content, title='Database')
def delete_form(name, cancel=None): """produce a delete form""" css = """ .delete-card { border: thin solid #ddd; margin: 10% auto; width: 50%; padding: 3em; background: white; box-shadow: 3px 3px 3px #ddd; } .delete-card p { font-size: 1.8rem; } @media (max-width: 600px) { .delete-card { padding: 1em; width: 100%; } } """ return zoom.Component(html.div(Form( MarkdownText('Are you sure you want to delete **%s**?' % name), Hidden(name='confirm', value='no'), Button('Yes, I\'m sure. Please delete.', name='delete_button', cancel=cancel or url_for('..'))).edit(), classed='delete-card'), css=css)
def setUp(self): bunch = zoom.utils.Bunch # db = zoom.database.setup_test() zoom.system.site = zoom.sites.Site() zoom.system.request = bunch( app=bunch(name='myapp', url='/myapp'), user=zoom.system.site.users.first(username='******')) zoom.system.parts = zoom.Component()
def requires(*package_names): """Inform framework of the packages required for rendering >>> import zoom.request >>> request = zoom.request.Request(dict(PATH_INFO='/')) >>> zoom.system.site = zoom.site.Site(request) >>> zoom.system.parts = zoom.Component() >>> requires('c3') >>> libs = zoom.system.parts.parts['libs'] >>> print('\\n'.join(list(libs))) https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.15/c3.min.js >>> zoom.system.parts = zoom.Component() >>> requires('jquery-ui') >>> libs = zoom.system.parts.parts['libs'] >>> print('\\n'.join(list(libs))) //code.jquery.com/jquery-3.3.1.min.js //code.jquery.com/ui/1.12.1/jquery-ui.min.js >>> try: ... requires('d4') ... except Exception as e: ... 'Missing required' in str(e) and 'raised!' 'raised!' """ parts = zoom.Component() registered_packages = get_registered_packages() for name in package_names: package = registered_packages.get(name) if package: requirements = package.get('requires') if requirements: requires(*requirements) parts += zoom.Component(**package) else: missing = set(package_names) - set(registered_packages) raise Exception('Missing required packages: {}'.format(missing)) zoom.system.parts += parts
def format(self, *content, title=None, footer=None): # pylint: disable=arguments-differ """Card format Format content in the form of a Card. """ header = html.div(title, classed='card-header') if title else '' footer = html.div(footer, classed='card-footer') if footer else '' card = zoom.Component( html.div(header, html.div(zoom.Component(content).render(), classed='card-body'), footer, classed='card')) return zoom.DynamicComponent( self, card, )
def _index(self): self.model.site.logging = False db = self.model.site.db content = zoom.Component( views.index_metrics_view(db), views.IndexPageLayoutView( feed1=activity_panel(db), feed2=users_panel(db), feed3=error_panel(db), feed4=warning_panel(db), ), ) return zoom.partial(content)
def get_impersonation_notice(): username = get_impersonated_username() if username is None: return '' return str( zoom.Component( h.div( 'Impersonating {}'.format(username), zoom.link_to( 'Stop Impersonating', '/stop-impersonation', classed='action' ), id='impersonation-notice' ) ) )
def callback(method, url=None, timeout=5000): method_name = method.__name__ path = url or '/<dz:app_name>/' + method_name js = """ jQuery(function($){ setInterval(function(){ $.get('%(path)s', function( content ){ if (content) { $('#%(method_name)s').html( content ); } }); }, %(timeout)s); }); """ % dict(path=path, method_name=method_name, timeout=timeout) content = '<div id="%(method_name)s">%(initial_value)s</div>' % dict( initial_value=method().content, method_name=method_name) return zoom.Component(content, js=js)
def view(): hr = '<hr>\n' content = zoom.Component( 'zoom.components.HeaderBar', c.HeaderBar(left=h.h2('HeaderBar Left'), right='HeaderBar right'), hr, 'zoom.components.spinner', h.div(c.spinner(), classed="clearfix", style="margin: 40px auto; width: 0;"), hr, 'zoom.components.dropzone', c.dropzone('/sample/components'), hr, ) return zoom.page(content, title='Components')
def index(self, q=''): content = callback(self._index) if q: request = zoom.system.request users_collection = users.get_users_collection(request) user_records = users_collection.search(q) groups_collection = groups.get_groups_collection(request) group_records = groups_collection.search(q) if user_records or group_records: content = zoom.Component() if group_records: footer = '%d groups found' % len(group_records) content += zoom.browse( group_records, columns=groups_collection.columns, labels=groups_collection.labels, title='Groups', footer=footer, ) if user_records: footer = '%d users found' % len(user_records) content += zoom.browse( user_records, columns=users_collection.columns, labels=users_collection.labels, title='Users', footer=footer, ) else: zoom.alerts.warning('no records found') return page( content, title='Overview', search=q )
def view(): hr = '<hr>\n' data = [('String', 'Integer', 'Decimal'), ('One', 1, decimal.Decimal(1234)), ('Two', 2, decimal.Decimal(2345))] content = zoom.Component( 'zoom.browse', zoom.browse(data, title='Sample Header', footer='sample footer'), hr, 'zoom.components.HeaderBar', c.HeaderBar(left=h.h2('HeaderBar Left'), right='HeaderBar right'), hr, 'zoom.components.spinner', h.div(c.spinner(), classed="clearfix", style="margin: 40px auto; width: 0;"), hr, 'zoom.components.dropzone', c.dropzone('/sample/components'), hr, use_common_package('not updated yet')) return zoom.page(content, title='Components')
def dropzone(url, **kwargs): """Dropzone component A basic dropzone component that supports drag and drop uploading of files which are posted to the URL provided. >>> zoom.system.site = zoom.sites.Site() >>> zoom.system.site.packages = {} >>> zoom.system.request = zoom.utils.Bunch(app=zoom.utils.Bunch(name='hello', packages={}, common_packages={})) >>> c = dropzone('/app/files') >>> isinstance(c, zoom.Component) True """ zoom.requires('dropzone') id = 'dropzone_' + uuid.uuid4().hex js = """ var %(id)s = new Dropzone("#%(id)s", {url: "%(url)s"}); """ % dict(id=id, url=url) html = div(classed='dropzone', id=id, **kwargs) return zoom.Component(html)#, js=js)
def partial(*args, **kwargs): """Return a partial HTML response.""" content = zoom.Component(*args, **kwargs) return zoom.response.HTMLResponse(content.render())
def format(self, chart): """Format a Chart""" zoom.requires('pivot-table') return (zoom.Component( "<div class='pivot-table' id='%s'></div>" % chart.selector) + zoom.DynamicComponent.format(self, chart=chart))
def use_common_package(message): zoom.requires('common_package_test') return zoom.Component(h.tag('div', message, id='common_package_test'))
def configuration(self): """Return the configuration page""" get = zoom.system.site.config.get site = zoom.system.site request = zoom.system.request app = zoom.system.request.app system_apps = get('apps', 'system', ','.join(zoom.apps.DEFAULT_SYSTEM_APPS)) main_apps = get('apps', 'main', ','.join(zoom.apps.DEFAULT_MAIN_APPS)) items = zoom.packages.get_registered_packages() packages = ((key, '<br>'.join( '{resources}'.format(resources='<br>'.join(resources)) for resource_type, resources in sorted( parts.items(), key=lambda a: ['requires', 'styles', 'libs'].index(a[0])))) for key, parts in sorted(items.items())) return page(zoom.Component( h.h2('Site'), zoom.html.table([(k, getattr(site, k)) for k in ( 'name', 'path', 'owner_name', 'owner_email', 'owner_url', 'admin_email', 'csrf_validation', )]), h.h2('Users'), zoom.html.table([(k, getattr(site, k)) for k in ( 'guest', 'administrators_group', 'developers_group', )]), h.h2('Apps'), zoom.html.table([(k, getattr(site, k)) for k in ( 'index_app_name', 'home_app_name', 'login_app_name', 'auth_app_name', 'locate_app_name', )] + [ ('app.path', app.path), ('apps_paths', '<br>'.join(site.apps_paths)), ('main_apps', main_apps), ('system_apps', system_apps), ]), h.h2('Theme'), zoom.html.table([ ('name', site.theme), ('path', site.theme_path), ('comments', site.theme_comments), ]), h.h2('Sessions'), zoom.html.table([(k, getattr(site, k)) for k in ('secure_cookies', )]), h.h2('Monitoring'), zoom.html.table([ ('logging', site.logging), ('profiling', site.profiling), ('app_database', site.monitor_app_database), ('system_database', site.monitor_system_database), ]), h.h2('Errors'), zoom.html.table([ ('users', get('errors', 'users', False)), ]), h.h2('Packages'), zoom.html.table(packages, ), css=""" .content table { width: 100%; } .content table td { vertical-align: top; width: 70%; } .content table td:first-child { width: 25%; } """), title='Environment')
def component(self): return zoom.Component( '<h1>Component</h1>This is a <code>Component</code> response.')
def browse(data, **kwargs): """browse data""" def getcol(item, index): if isinstance(item, dict): return item.get(index, None) elif isinstance(item, (tuple, list)): return item[index] else: return getattr(item, index) labels = kwargs.get('labels') fields = kwargs.get('fields') columns = kwargs.get('columns') title = kwargs.get('title') actions = kwargs.get('actions', []) header = kwargs.get('header') footer = kwargs.get('footer', '') sortable = kwargs.get('sortable', False) table_id = kwargs.get('table_id', uuid.uuid4().hex) items = list(data) if labels: if not columns: if len(items) and hasattr(items[0], 'get'): columns = [name_for(label).lower() for label in labels] elif len(items) and hasattr(items[0], '__len__') and len(items[0]): columns = range(len(labels)) else: columns = [name_for(label).lower() for label in labels] else: if columns: labels = columns else: if len(items) and isinstance(items[0], Record): labels = columns = sorted_column_names(set([ a for item in items for a in item.attributes() if not a.startswith('__') ])) elif (len(items) and hasattr(items[0], 'keys') and callable(getattr(items[0], 'keys'))): # list of dicts labels = columns = [ a for a in sorted_column_names(items[0].keys()) if not a.startswith('__') ] elif len(items) and hasattr(items[0], '__dict__'): # list of objects labels = columns = [items[0].__dict__.keys()] elif hasattr(data, 'cursor'): # Result object labels = [c[0] for c in data.cursor.description] columns = range(len(labels)) elif len(items) and hasattr(items[0], '__len__') and len(items[0]): # list of lists? labels = items[0] columns = range(len(items[0])) items = items[1:] else: if len(items): raise Exception('%s' % hasattr(items[0],'__len__')) return '<div class="baselist"><table><tbody><tr><td>None</td></th></tbody></table></div>' columns = list(columns) labels = list(labels) invisible = [] formatters = [] alignments = [] if fields: invisible_labels = [] lookup = fields.as_dict() for n, col in enumerate(columns): if col in lookup: field = lookup[col] better_label = field.label visible = field.visible and field.browse if visible: alignments.append(field.alignment) formatters.append(str) else: better_label = None visible = True values = [getcol(row, col) for row in items] formatter, alignment = get_format(col, values) alignments.append(alignment) formatters.append(formatter) if better_label: if n > len(labels): labels.append(better_label) else: labels[n] = better_label if not visible: invisible.append(col) invisible_labels.append(n) for n in reversed(invisible_labels): del labels[n] alist = [] for item in items: fields.initialize(item) flookup = fields.display_value() row = [ flookup.get(col, getcol(item, col)) for col in columns if col not in invisible ] alist.append(row) else: alist = [[getcol(item, col) for col in columns] for item in items] styling = calculate_styling(columns, labels, alist) formatters = [s[0] for s in styling] alignments = [s[1] for s in styling] column_alignments = """ #%s tbody>tr>td:nth-child(%s) { text-align: right; padding-right: 20px; } #%s thead>tr>th:nth-child(%s) { text-align: right; padding-right: 20px; } """ alignment_css = ''.join( column_alignments % (table_id, n+1, table_id, n+1) for n, alignment in enumerate(alignments) if alignment == 'right' ) css = alignment_css t = [] if labels: t.append('<thead><tr>') colnum = 0 for label in labels: colnum += 1 t.append('<th>%s</th>' % label) t.append('</tr></thead>') t.append('<tbody>') count = 0 for row in alist: count += 1 t.append('<tr id="row-%s">' % (count)) for n, item in enumerate(row): try: value = formatters[n].format(item) except BaseException: value = repr(item) wrapping = len(value) < 80 and ' nowrap' or '' cell_tpl = '<td{}>%s</td>'.format(wrapping) t.append(cell_tpl % value) t.append('</tr>') t.append('</tbody>') if not count: t.append('<tr><td colspan=%s>None</td></tr>' % len(labels)) if not header: if title: header = '<div class="title">%s</div>' % title if actions: header += as_actions(actions) header_body = header and ('<div class="header">%s</div>' % header) or '' footer_body = footer and ('<div class="footer">%s</div>' % footer) or '' if sortable: zoom.requires('datatables') js = """ $('#{}').DataTable( {{ "lengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]], "dom": '<if>rt<lp><"clear">', paging: false, "oLanguage": {{ "sSearch": "Filter" }} }} ); """.format(table_id) css += """ .dataTables_filter label { font-weight: normal; } """ zoom.Component(css=css, js=js).render() else: zoom.Component(css=css).render() table_tag = '<table id="{}">'.format(table_id) result = '\n'.join( ['<div class="baselist">'] + [header_body] + [table_tag] + t + ['</table>'] + [footer_body] + ['</div>'] ) return result
def view(): layout = WidgetsLayout() card = Card() cards = Cards().format(cards=zoom.Component( card.format('basic card'), card.format('card with title', title='Title'), card.format('Card with footer', footer='Footer'), card.format('Card with title and footer', title='Title', footer='Footer'), ), ) metric = MetricWidget() metrics = zoom.Component( h.h2('Metrics'), zoom.html.div( layout.format( card(metric(title='Queries', value=100, hint='numeric value')), card(metric(title='Items', value='1,123', hint='string')), card(metric(title='Revenue', value='$1,323', hint='formatted')), card( metric(title='Date', value=zoom.tools.today(), hint='other types')), ), classed='metrics-layout', ), css='.metrics-layout .widgets-layout { min-height: auto; }') progress_widgets = (card(ProgressWidget().format( 10 + n * 20, title='Metric %s' % n, hint='Metric %s hint' % n if n in [1, 3] else '', )) for n in range(4)) progress = zoom.Component( h.h2('Progress'), layout.format(*progress_widgets), ) charts = [ Chart( title='Pipeline', format='${:,.2f}', ), Chart( title='Inventory', classed='bg-gradient-info', smooth=False, ), Chart( title='Expenses', classed='bg-gradient-warning', data=[100, 200, 700, 400], labels=['January', 'February', 'March', 'April'], type='bar', ), Chart( title='Errors', classed='bg-gradient-danger', fill_color='#fff', ), ] chart_widget = ChartWidget() chart_widgets = (card(chart_widget.format(chart)) for chart in charts) charts_section = zoom.Component(h.h2('Charts'), layout.format(*chart_widgets)) return zoom.page(cards, metrics, progress, charts_section, title='Widgets')
def test_respond_component(self): response = zoom.apps.respond(zoom.Component('my html', css='my css'), self.request) self.assertEqual(type(response), zoom.response.HTMLResponse)