def render_action_button(self, url, button_text, icon, size='small', row_id=None, user_signature=None, page=None): separator = '?' if row_id: url += '/%s' % (row_id) if user_signature: url += '%suser_signature=%s' % (separator, user_signature) separator = '&' if page: url += '%spage=%s' % (separator, page) if self.include_action_button_text: _a = A(_href=url, _class='button is-%s' % size, _title=button_text) _span = SPAN(_class='icon is-%s' % size) _span.append(I(_class='fas %s' % icon)) _a.append(_span) _a.append(SPAN(button_text)) else: _a = A(I(_class='fas %s' % icon), _href=url, _class='button is-%s' % size, _title=button_text) return _a
def script(self): js = ( '<script type="text/javascript">' ' $(document).ready(function() {' ' $(\'#datatables_table\').DataTable( {' ' dom: "lfrtip", ' ' processing: true, ' ' serverSide: true, ' ' lengthMenu: [ [10, 15, 20, -1], [10, 15, 20, \'All\'] ], ' ' pageLength: %s, ' ' pagingType: "numbers", ' ' ajax: "%s", ' ' columns: [' % (self.page_length, self.data_url)) # add the field values for field in self.fields: js += ( '{' ' "data": "%s", ' ' "name": "%s", ' ' "visible": %s, ' '},' % (field.name, field.name, 'true' if field.visible else 'false')) # add the row buttons js += ( '{' ' data: null,' ' render: function ( data, type, row ) {' ' var edit_url=\'%s\'; ' ' edit_url = edit_url.replace("record_id", row.DT_RowId); ' ' var delete_url=\'%s\'; ' ' delete_url = delete_url.replace("record_id", row.DT_RowId); ' ' return edit_url + " " + delete_url ' ' }, ' ' orderable: false, ' '}' % (A(I(_class='fas fa-edit'), _href=self.edit_url if self.edit_url else '#', _class='button is-small'), A(I(_class='fas fa-trash'), _href=self.delete_url if self.delete_url else '#', _class='button is-small', _message='Delete Record'))) js += '], columnDefs: [' for index, field in enumerate(self.fields): if not field.visible: js += '{"visible": false, "targets": %s},' % index js += '{className: "has-text-centered", "targets": %s}' % (index + 1) js += ('],' 'order: ') for sort in self.sort_sequence: js += '[ %s, "%s" ]' % (sort[0], sort[1]) js += (',' ' stateSave: true, ' ' select: true, ' ' });' ' $(".dataTables_filter input").focus().select();' '});' '</script>') return str(js)
def render_table_header(self): up = I(**self.param.grid_class_style.get("grid-sorter-icon-up")) dw = I(**self.param.grid_class_style.get("grid-sorter-icon-down")) columns = [] sort_order = request.query.get("orderby", "") for index, field in enumerate(self.param.fields): if field.readable and (field.type != "id" or self.param.show_id): key = "%s.%s" % (field.tablename, field.name) heading = ( self.param.headings[index] if index < len(self.param.headings) else field.label ) heading = title(heading) # add the sort order query parm sort_query_parms = dict(self.query_parms) attrs = {} if isinstance(field, FieldVirtual): col = DIV(heading) elif key == sort_order: sort_query_parms["orderby"] = "~" + key url = URL(self.endpoint, vars=sort_query_parms) attrs = self.attributes_plugin.link(url=url) col = A(heading, up, **attrs) else: sort_query_parms["orderby"] = key url = URL(self.endpoint, vars=sort_query_parms) attrs = self.attributes_plugin.link(url=url) col = A(heading, dw if "~" + key == sort_order else "", **attrs) columns.append((key, col)) thead = THEAD(_class=self.param.grid_class_style.classes.get("grid-thead", "")) for key, col in columns: col_class = " grid-col-%s" % key thead.append( TH( col, _class=self.param.grid_class_style.classes.get("grid-th", "") + col_class, _style=self.param.grid_class_style.styles.get("grid-th"), ) ) if ( self.param.details or self.param.editable or self.param.deletable or self.param.pre_action_buttons or self.param.post_action_buttons ): thead.append( TH("", **self.param.grid_class_style.get("grid-th-action-button")) ) return thead
def api(self, id=None): """Returns data according to the API request.""" # Builds the header. header = dict( is_header=True, cells=[ dict(text="First Name", sortable=True), dict(text="Last Name", sortable=True), dict(text="Arrival Time", sortable=True), dict(text="", sortable=False), # Icons ], ) # Gets the request parameters, and copies the sort order in the header. req = self._get_request_params(header) timezone = request.query.get("timezone") q = request.query.get("q", "") # Query string # Forms the query. if q: query = db( db.vue_form_table.first_name.contains(q) | db.vue_form_table.last_name.contains(q)) else: query = db.vue_form_table # Forms the select. rows = db(query).select(**req.search_args) # Builds the result rows. result_rows = [] for r in rows: cells = [] cells.append(dict(text=r.first_name)) cells.append(dict(text=r.last_name)) cells.append(dict(text=r.arrival_time.isoformat(), type="date")) cells.append( dict(raw_html=SPAN( A( I(_class="fa fa-eye"), _href=URL("vue_view_form", r.id, signer=self.signer), ), " ", A( I(_class="fa fa-pen"), _href=URL("vue_edit_form", r.id, signer=self.signer), ), ).xml())) result_rows.append( dict(cells=cells, delete=URL("delete_row", r.id, signer=self.signer))) has_more, result_rows = self._has_more(result_rows) return dict( page=req.page, has_search=True, has_delete=True, search_placeholder="", has_more=has_more, rows=[header] + result_rows, )
def example_html_grid(path=None): # controllers and used for all grids in the app grid_param = dict(rows_per_page=5, include_action_button_text=True, search_button_text="Filter", formstyle=FormStyleDefault, grid_class_style=GridClassStyle) search_queries = [ ['By Name', lambda value: db.thing.name.contains(value)], ['By Color', lambda value: db.thing.color == value], [ 'By Name or Color', lambda value: db.thing.name.contains(value) | (db.thing.color == value) ], ] query = db.thing.id > 0 orderby = [db.thing.name] grid = Grid(path, query, fields=[field for field in db.thing if field.readable], search_queries=search_queries, orderby=orderby, **grid_param) grid.formatters['thing.color'] = lambda color: I(_class="fa fa-circle", _style="color:" + color) return dict(grid=grid)
def table(self): _html = DIV() if self.create_url and self.create_url != '': _a = A('', _href=self.create_url, _class='button', _style='margin-bottom: 1rem;') _span = SPAN(_class='icon is-small') _span.append(I(_class='fas fa-plus')) _a.append(_span) _a.append(SPAN('New')) _html.append(_a) _table = TABLE(_id='datatables_table', _class='compact stripe hover cell-border order-column', _style='padding-top: 1rem;') _thead = THEAD() _tr = TR() for field in self.fields: _tr.append(TH(field.label, _class='datatables-header')) _tr.append( TH('ACTIONS', _class='datatables-header has-text-centered', _style='color: black; width: 1px; white-space: nowrap;')) _thead.append(_tr) _table.append(_thead) _table.append(TBODY()) _html.append(_table) return str(_html)
def render_field(self, row, field): """ Render a field if only 1 table in the query, the no table name needed when getting the row value - however, if there are multiple tables in the query (self.use_tablename == True) then we need to use the tablename as well when accessing the value in the row object the row object sent in can take :param row: :param field: :return: """ if self.use_tablename: field_value = row[field.tablename][field.name] else: field_value = row[field.name] if field.type == 'date': _td = TD(XML("<script>\ndocument.write(" "moment(\"%s\").format('L'));\n</script>" % field_value) \ if row and field and field_value else '', _class='has-text-centered') elif field.type == 'boolean': # True/False - only show on True, blank for False if row and field and field_value: _td = TD(_class='has-text-centered') _span = SPAN(_class='icon is-small') _span.append(I(_class='fas fa-check-circle')) _td.append(_span) else: _td = TD(XML(' ')) else: _td = TD(field_value if row and field and field_value else '') return _td
def render_action_button( self, url, button_text, icon, icon_size="small", additional_classes=None, additional_styles=None, override_classes=None, override_styles=None, message=None, onclick=None, row_id=None, name="grid-button", row=None, **attr, ): separator = "?" if row_id: url += "/%s" % row_id classes = self.param.grid_class_style.classes.get(name, "") styles = self.param.grid_class_style.styles.get(name, "") def join(items): return ( " ".join(items) if isinstance(items, (list, tuple)) else " %s" % items ) if override_classes: classes = join(override_classes) elif additional_classes: classes += join(additional_classes) if override_styles: styles = join(override_styles) elif additional_styles: styles += join(additional_styles) if callable(url): url = url(row) link = A( I(_class="fa %s" % icon), _href=url, _role="button", _class=classes, _message=message, _title=button_text, _style=styles, **attr, ) if self.param.include_action_button_text: link.append( XML( '<span class="grid-action-button-text"> %s</span>' % button_text ) ) return link
def render_table_header(self): _thead = THEAD() for index, field in enumerate(self.fields): if field.name not in [x.name for x in self.hidden_fields] and ( field.name != 'id' or (field.name == 'id' and self.show_id)): try: heading = self.headings[index] except: if field.table == self.tablename: heading = field.label else: heading = str(field.table) # add the sort order query parm sort_query_parms = dict(self.query_parms) sort_query_parms['sort'] = index current_sort_dir = 'asc' if '%s.%s' % (field.tablename, field.name) in self.storage_values['orderby']: sort_query_parms['sort'] = -index _h = A(heading.replace('_', ' ').upper(), _href=URL(self.endpoint, vars=sort_query_parms)) _h.append(SPAN(I(_class='fas fa-sort-up'), _class='is-pulled-right')) elif '~%s.%s' % (field.tablename, field.name) in self.storage_values['orderby']: _h = A(heading.replace('_', ' ').upper(), _href=URL(self.endpoint, vars=sort_query_parms)) _h.append(SPAN(I(_class='fas fa-sort-down'), _class='is-pulled-right')) else: _h = A(heading.replace('_', ' ').upper(), _href=URL(self.endpoint, vars=sort_query_parms)) if 'sort_dir' in sort_query_parms: current_sort_dir = sort_query_parms['sort_dir'] del sort_query_parms['sort_dir'] if index == int(request.query.get('sort', 0)) and current_sort_dir == 'asc': sort_query_parms['sort_dir'] = 'desc' _th = TH() _th.append(_h) _thead.append(_th) if self.editable or self.deletable: _thead.append(TH('ACTIONS', _style='text-align: center; width: 1px; white-space: nowrap;')) return _thead
def render_table_header(self): up = I(**self.param.grid_class_style.get("grid-sorter-icon-up")) dw = I(**self.param.grid_class_style.get("grid-sorter-icon-down")) columns = [] sort_order = request.query.get("orderby", "") for index, field in enumerate(self.param.fields): if field.readable and (field.type != "id" or self.param.show_id): key = "%s.%s" % (field.tablename, field.name) heading = (self.param.headings[index] if index < len(self.param.headings) else field.label) heading = title(heading) # add the sort order query parm sort_query_parms = dict(self.query_parms) if key == sort_order: sort_query_parms["orderby"] = "~" + key href = URL(self.endpoint, vars=sort_query_parms) col = A(heading, up, _href=href) else: sort_query_parms["orderby"] = key href = URL(self.endpoint, vars=sort_query_parms) col = A(heading, dw if "~" + key == sort_order else "", _href=href) columns.append((key, col)) thead = THEAD() for key, col in columns: col_class = "grid-col-%s" % key thead.append( TH( col, _class=self.param.grid_class_style.classes.get( "grid-th", "") + col_class, _style=self.param.grid_class_style.styles.get("grid-th"), )) if self.param.details or self.param.editable or self.param.deletable: thead.append( TH("", **self.param.grid_class_style.get('grid-th-action-button'))) return thead
def employees(path=None): queries = [(db.employee.id > 0)] orderby = [db.employee.last_name, db.employee.first_name] search_queries = [['Search by Company', lambda val: db.company.id == val, db.employee.company.requires], ['Search by Department', lambda val: db.department.id == val, db.employee.department.requires], ['Search by Name', lambda val: "first_name || ' ' || last_Name LIKE '%%%s%%'" % val]] search = GridSearch(search_queries, queries) fields = [db.employee.id, db.employee.first_name, db.employee.last_name, db.company.name, db.department.name, db.employee.hired, db.employee.supervisor, db.employee.active] grid = Grid(path, search.query, search_form=search.search_form, fields=fields, left=[db.company.on(db.employee.company == db.company.id), db.department.on(db.employee.department == db.department.id)], orderby=orderby, create=True, details=True, editable=True, deletable=True, **GRID_DEFAULTS) grid.formatters_by_type['boolean'] = lambda value: SPAN(I(_class='fas fa-check-circle')) if value else "" grid.formatters_by_type['date'] =lambda value: XML( '<script>document.write((new Date(%s,%s,%s)).toLocaleDateString({month: "2-digit", day: "2-digit", year: "numeric"}).split(",")[0])</script>' % (value.year, value.month, value.day,) ) return dict(grid=grid)
def example_html_grid(path=None): # controllers and used for all grids in the app grid_param = dict( rows_per_page=5, include_action_button_text=True, search_button_text="Filter", formstyle=FormStyleDefault, grid_class_style=GridClassStyle, ) search_queries = [ ["By Name", lambda value: db.thing.name.contains(value)], ["By Color", lambda value: db.thing.color == value], [ "By Name or Color", lambda value: db.thing.name.contains(value) | (db.thing.color == value), ], ] query = db.thing.id > 0 orderby = [db.thing.name] columns = [field for field in db.thing if field.readable] columns.insert(0, Column("Custom", lambda row: A("click me"))) grid = Grid(path, query, columns=columns, search_queries=search_queries, orderby=orderby, show_id=False, T=T, **grid_param) grid.formatters["thing.color"] = lambda color: I(_class="fa fa-circle", _style="color:" + color) return dict(grid=grid)