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, 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 render_table_pager(self): pager = DIV(**self.param.grid_class_style.get("grid-pagination")) previous_page_number = None for page_number in self.iter_pages( self.current_page_number, self.number_of_pages ): pager_query_parms = dict(self.query_parms) pager_query_parms["page"] = page_number # if there is a gat add a spacer if previous_page_number and page_number - previous_page_number > 1: pager.append(SPAN("...", _style="margin:0 10px;")) is_current = self.current_page_number == page_number page_name = ( "grid-pagination-button-current" if is_current else "grid-pagination-button" ) attrs = self.attributes_plugin.link( url=URL(self.endpoint, vars=pager_query_parms) ) pager.append( A( page_number, **self.param.grid_class_style.get(page_name), _role="button", **attrs, ) ) previous_page_number = page_number return pager
def render_table_pager(self): pager = DIV(**self.param.grid_class_style.get("grid-pagination")) previous_page_number = None for page_number in self.iter_pages(self.current_page_number, self.number_of_pages): pager_query_parms = dict(self.query_parms) pager_query_parms["page"] = page_number # if there is a gat add a spacer if previous_page_number and page_number - previous_page_number > 1: pager.append(SPAN("...", _style="margin:0 10px;")) is_current = self.current_page_number == page_number page_name = ("grid-pagination-button-current" if is_current else "grid-pagination-button") attrs = ({ "_hx-get": URL(self.endpoint, vars=pager_query_parms), "_hx-target": self.param.htmx_target, "_hx-swap": "innertHTML", } if self.param.htmx_target else { "_href": URL(self.endpoint, vars=pager_query_parms) }) pager.append( A( page_number, **self.param.grid_class_style.get(page_name), _role="button", **attrs, )) previous_page_number = page_number return pager
def edit_phone(contact_id=None, phone_id=None): assert contact_id is not None assert phone_id is not None # match the phone owner's id as well as the number's id row = db( (db.phone.contact_id == contact_id) & (db.phone.id == phone_id) ).select().first() if row is None: redirect(URL('edit_phones', contact_id)) contact_inf = db(db.contact.id == contact_id).select().first() if contact_inf.user_email != get_user_email(): abort(403) form = Form([Field('Phone', 'string'), Field('Kind', 'string')], record=dict(Phone=row.number, Kind=row.phone_type), deletable=False, csrf_session=session, formstyle=FormStyleBulma) form.param.sidecar.append(SPAN(" ", A("Back", _class="button", _href=URL('edit_phones', contact_id)))) if form.accepted: # insert the number for that contact number = form.vars['Phone'] phone_type = form.vars['Kind'] db((db.phone.contact_id == contact_id) & (db.phone.id == phone_id)).update( number=number, phone_type=phone_type ) redirect(URL('edit_phones', contact_id)) contact_inf = db.contact[contact_id] first_name = contact_inf.first_name last_name = contact_inf.last_name name = f"{first_name} {last_name}" return dict(form=form, name=name)
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 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 test_find(self): a = DIV('A', _class='a') b = SPAN('B', _id='b') div = DIV(DIV(a), DIV(b)) self.assertEqual(div.find('.a')[0], a) self.assertEqual(div.find('#b')[0], b) self.assertEqual(div.find('div.a')[0], a) self.assertEqual(div.find('span#b')[0], b) self.assertEqual(div.find('span')[0], b) self.assertEqual(len(div.find('div')), 4) self.assertEqual(len(div.find('abc')), 0)
def footer(self, count, offset, len_items): """generates the footer""" T = self.T page = int(offset / self.limit) last_page = math.floor(count / self.limit) return DIV( A("First", _href=self.url(0), _class="button") if page > 1 else "", A("Previous", _href=self.url(page - 1), _class="button") if page > 0 else "", SPAN( T("page %s/%s (%s/%s records)") % (page + 1, last_page + 1, len_items, count)), A("Next", _href=self.url(page + 1), _class="button") if page < last_page else "", A("Last", _href=self.url(last_page - 1), _class="button") if page < last_page - 1 else "", SCRIPT(Grid.script), _class="py4web-grid-footer", )
def add_phone(contact_id): assert contact_id is not None # contact_id is the id of the contact for which we are inserting an additional number form = Form([Field('Phone', 'string'), Field('Kind', 'string')], csrf_session=session, formstyle=FormStyleBulma) form.param.sidecar.append(SPAN(" ", A("Back", _class="button", _href=URL('edit_phones', contact_id)))) if form.accepted: # insert the number for that contact number = form.vars['Phone'] phone_type = form.vars['Kind'] db.phone.insert( contact_id=contact_id, number=number, phone_type=phone_type ) redirect(URL('edit_phones', contact_id)) contact_inf = db.contact[contact_id] first_name = contact_inf.first_name last_name = contact_inf.last_name name = f"{first_name} {last_name}" return dict(form=form, name=name)
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 __call__( self, table, vars, errors, readonly, deletable, noncreate, show_id, kwargs=None, ): kwargs = kwargs if kwargs else {} form_method = "POST" form_action = request.url.split(":", 1)[1] form_enctype = "multipart/form-data" form = FORM(_method=form_method, _action=form_action, _enctype=form_enctype, **kwargs) controls = Param( labels=dict(), widgets=dict(), wrappers=dict(), comments=dict(), hidden_widgets=dict(), placeholders=dict(), titles=dict(), errors=dict(), begin=XML(form.xml().split("</form>")[0]), submit="", delete="", end=XML("</form>"), ) json_controls = dict(form_fields=[], form_values=dict(), form_buttons=[], form_method=form_method, form_action=form_action, form_enctype=form_enctype, **kwargs) class_label = self.classes["label"] class_outer = self.classes["outer"] class_inner = self.classes["inner"] class_error = self.classes["error"] class_info = self.classes["info"] all_fields = [x for x in table] if "_virtual_fields" in dir(table): all_fields += table._virtual_fields for field in all_fields: # Reset the json control fields. field_attributes = dict() field_value = None field_name = field.name field_type = field.type field_comment = field.comment if field.comment else "" field_label = field.label input_id = "%s_%s" % (field.tablename, field.name) if isinstance(field, FieldVirtual): value = None else: value = vars.get( field.name, field.default() if callable(field.default) else field.default, ) error = errors.get(field.name) field_class = "type-" + field.type.split()[0].replace(":", "-") placeholder = (field._placeholder if "_placeholder" in field.__dict__ else None) title = field._title if "_title" in field.__dict__ else None field_disabled = False # only display field if readable or writable if not field.readable and not field.writable: continue # if this is a readonly field only show readable fields if readonly: if not field.readable: continue # do not show the id if not desired if field.type == "id" and not show_id: continue # if this is an create form (unkown id) then only show writable fields. # Some if an edit form was made from a list of fields and noncreate=True elif not vars.get("id") and noncreate: if not field.writable: continue # ignore blob fields if field.type == "blob": # never display blobs (mistake?) continue # ignore fields of type id its value is equal to None if field.type == "id" and value is None: field.writable = False continue # if the form is readonly or this is an id type field, display it as readonly if (readonly or not field.writable or field.type == "id" or isinstance(field, FieldVirtual)): # for boolean readonly we use a readonly checbox if field.type == "boolean": control = CheckboxWidget().make(field, value, error, title, readonly=True) # for all othe readonly fields we use represent or a string else: if isinstance(field, FieldVirtual): field_value = field.f(vars) else: field_value = (field.represent and field.represent(value) or value or "") field_type = "represent" control = DIV(field_value) field_disabled = True # if we have a field.widget for the field use it but this logic is deprecated elif field.widget: control = field.widget(table, vars) # else we pick the right widget else: if field.name in self.widgets: widget = self.widgets[field.name] elif field.type == "text": widget = TextareaWidget() elif field.type == "datetime": widget = DateTimeWidget() elif field.type == "boolean": widget = CheckboxWidget() elif field.type == "upload": widget = FileUploadWidget() url = getattr(field, "download_url", lambda value: "#")(value) # Set the download url. field_attributes["_download_url"] = url # Set the flag determining whether the file is an image. field_attributes["_is_image"] = ( url != "#") and Form.is_image(value) # do we need the variables below? delete_field_attributes = dict() delete_field_attributes["_label"] = "Remove" delete_field_attributes["_value"] = "ON" delete_field_attributes["_type"] = "checkbox" delete_field_attributes["_name"] = "_delete_" + field.name json_controls["form_fields"] += [delete_field_attributes] json_controls["form_values"]["_delete_" + field.name] = None elif get_options(field.requires) is not None: widget = SelectWidget() elif field.type == "password": widget = PasswordWidget() elif field.type.startswith("list:"): widget = ListWidget() else: widget = Widget() control = widget.make(field, value, error, title, placeholder) key = control.name.rstrip("/") if key == "input": key += "[type=%s]" % (control["_type"] or "text") control["_class"] = join_classes(control.attributes.get("_class"), self.classes.get(key)) # Set the form controls. controls["labels"][field_name] = field_label controls["widgets"][field_name] = control controls["comments"][field_name] = field_comment controls["titles"][field_name] = title controls["placeholders"][field_name] = placeholder # Set the remain json field attributes. field_attributes["_title"] = title field_attributes["_label"] = field_label field_attributes["_comment"] = field_comment field_attributes["_id"] = to_id(field) field_attributes["_class"] = field_class field_attributes["_name"] = field.name field_attributes["_type"] = field.type field_attributes["_placeholder"] = placeholder field_attributes["_error"] = error field_attributes["_disabled"] = field_disabled # Add to the json controls. json_controls["form_fields"] += [field_attributes] json_controls["form_values"][field_name] = field_value if error: controls["errors"][field.name] = error if field.type == "boolean": controls.wrappers[field.name] = wrapped = SPAN( control, _class=class_inner) form.append( DIV( wrapped, LABEL( " " + field.label, _for=input_id, _class=class_label, _style="display: inline !important", ), P(error, _class=class_error) if error else "", P(field.comment or "", _class=class_info), _class=class_outer, )) else: controls.wrappers[field.name] = wrapped = DIV( control, _class=self.class_inner_exceptions.get( control.name, class_inner), ) form.append( DIV( LABEL(field.label, _for=input_id, _class=class_label), wrapped, P(error, _class=class_error) if error else "", P(field.comment or "", _class=class_info), _class=class_outer, )) if vars.get("id"): form.append(INPUT(_name="id", _value=vars["id"], _hidden=True)) if deletable: deletable_record_attributes = dict() deletable_field_name = "_delete" deletable_field_type = "checkbox" # Set the deletable json field attributes. deletable_record_attributes["_label"] = " check to delete" deletable_record_attributes["_name"] = deletable_field_name deletable_record_attributes["_type"] = deletable_field_type deletable_record_attributes["_class"] = self.classes[ "input[type=checkbox]"] deletable_record_attributes["_value"] = "ON" # Add to the json controls. json_controls["form_fields"] += [deletable_record_attributes] json_controls["form_values"][deletable_field_name] = None controls["delete"] = INPUT( _type=deletable_field_type, _value=deletable_record_attributes["_value"], _name=deletable_field_name, _class=self.classes["input[type=checkbox]"], ) form.append( DIV( SPAN( controls["delete"], _class=class_inner, _stye="vertical-align: middle;", ), P( deletable_record_attributes["_label"], _class="help", _style="display: inline !important", ), _class=class_outer, )) submit_button_attributes = dict() submit_button_field_type = "submit" # Set the deletable json field attributes. submit_button_attributes["_label"] = "Submit" submit_button_attributes["_type"] = submit_button_field_type submit_button_attributes["_class"] = self.classes["input[type=submit]"] # Add to the json controls. json_controls["form_buttons"] += [submit_button_attributes] controls["submit"] = INPUT( _type=submit_button_field_type, _value="Submit", _class=self.classes["input[type=submit]"], ) submit = DIV( DIV( controls["submit"], _class=class_inner, ), _class=class_outer, ) form.append(submit) return dict(form=form, controls=controls, json_controls=json_controls)
def produce(self, table, vars, errors, readonly, deletable, classes=None): self.classes.update(classes or {}) form = FORM(_method="POST", _action=request.url, _enctype="multipart/form-data") controls = Param( labels=dict(), widgets=dict(), comments=dict(), hidden_widgets=dict(), placeholders=dict(), titles=dict(), errors=dict(), begin=XML(form.xml().split("</form>")[0]), submit="", delete="", end=XML("</form>"), ) class_label = self.classes["label"] class_outer = self.classes["outer"] class_inner = self.classes["inner"] class_error = self.classes["error"] class_info = self.classes["info"] for field in table: input_id = "%s_%s" % (field.tablename, field.name) value = vars.get(field.name, field.default) error = errors.get(field.name) field_class = "type-" + field.type.split()[0].replace(":", "-") placeholder = ( field._placeholder if "_placeholder" in field.__dict__ else None ) title = field._title if "_title" in field.__dict__ else None # only diplay field if readable or writable if not field.readable and not field.writable: continue # if this is a reaonly field only show readable fields if readonly: if not field.readable: continue # if this is an create form (unkown id) then only show writable fields elif not vars.get("id"): if not field.writable: continue # ignore blob fields if field.type == "blob": # never display blobs (mistake?) continue # ignore fields of type id its value is equal to None if field.type == "id" and value is None: field.writable = False continue # if the form is readonly or this is an id type field, display it as readonly if readonly or not field.writable or field.type == "id": if field.type == "boolean": control = INPUT( _type="checkbox", _id=input_id, _name=field.name, _value="ON", _disabled="", _checked=value, _title=title, ) else: control = DIV( field.represent and field.represent(value) or value or "" ) # if we have a widget for the field use it elif field.widget: control = field.widget(table, value) # else pick the proper default widget elif field.type == "text": control = TEXTAREA( value or "", _id=input_id, _name=field.name, _placeholder=placeholder, _title=title, ) elif field.type == "date": control = INPUT( _value=value, _type="date", _id=input_id, _name=field.name, _placeholder=placeholder, _title=title, ) elif field.type == "datetime": if isinstance(value, str): value = value.replace(" ", "T") control = INPUT( _value=value, _type="datetime-local", _id=input_id, _name=field.name, _placeholder=placeholder, _title=title, ) elif field.type == "time": control = INPUT( _value=value, _type="time", _id=input_id, _name=field.name, _placeholder=placeholder, _title=title, ) elif field.type == "boolean": control = INPUT( _type="checkbox", _id=input_id, _name=field.name, _value="ON", _checked=value, _title=title, ) elif field.type == "upload": control = DIV() if value and field.download_url is not None and not error: download_div = DIV() download_div.append( LABEL( "Currently: ", ) ) download_div.append( A(" download ", _href=field.download_url(value)) ) download_div.append( INPUT( _type="checkbox", _value="ON", _name="_delete_" + field.name, _title=title, ) ) download_div.append(" (check to remove)") control.append(download_div) control.append(LABEL("Change: ")) control.append(INPUT(_type="file", _id=input_id, _name=field.name)) elif get_options(field.requires) is not None and field.writable == True: multiple = field.type.startswith("list:") value = list(map(str, value if isinstance(value, list) else [value])) option_tags = [ OPTION(v, _value=k, _selected=(not k is None and k in value)) for k, v in get_options(field.requires) ] control = SELECT( *option_tags, _id=input_id, _name=field.name, _multiple=multiple, _title=title ) else: field_type = "password" if field.type == "password" else "text" if field.type.startswith("list:"): value = json.dumps(value or []) control = INPUT( _type=field_type, _id=input_id, _name=field.name, _value=None if field.type == "password" else value, _class=field_class, _placeholder=placeholder, _title=title, _autocomplete="off" if field_type == "password" else "on", ) key = control.name.rstrip("/") if key == "input": key += "[type=%s]" % (control["_type"] or "text") control["_class"] = ( control.attributes.get("_class", "") + " " + self.classes.get(key, "") ).strip() controls["labels"][field.name] = field.label controls["widgets"][field.name] = control controls["comments"][field.name] = field.comment if field.comment else "" controls["titles"][field.name] = title controls["placeholders"][field.name] = placeholder if error: controls["errors"][field.name] = error if field.type == "boolean": form.append( DIV( SPAN(control, _class=class_inner), LABEL( " " + field.label, _for=input_id, _class=class_label, _style="display: inline !important", ), P(error, _class=class_error) if error else "", P(field.comment or "", _class=class_info), _class=class_outer, ) ) else: form.append( DIV( LABEL(field.label, _for=input_id, _class=class_label), DIV(control, _class=class_inner), P(error, _class=class_error) if error else "", P(field.comment or "", _class=class_info), _class=class_outer, ) ) if vars.get("id"): form.append(INPUT(_name="id", _value=vars["id"], _hidden=True)) if deletable: controls["delete"] = INPUT( _type="checkbox", _value="ON", _name="_delete", _class=self.classes["input[type=checkbox]"], ) form.append( DIV( SPAN( controls["delete"], _class=class_inner, _stye="vertical-align: middle;", ), P( " check to delete", _class="help", _style="display: inline !important", ), _class=class_outer, ) ) controls["submit"] = INPUT( _type="submit", _value="Submit", _class=self.classes["input[type=submit]"], ) submit = DIV( DIV( controls["submit"], _class=class_inner, ), _class=class_outer, ) form.append(submit) return dict(form=form, controls=controls)
def large_text_renderer(key, value, rec): if isinstance(value, str) and len(value)>14: return SPAN(value[:10]+'...', _title=value)
def yfunc(xx, rr_id): xx = xx[:10] if rr_id % 2 == 0: return SPAN(xx, _style="color:red") else: return SPAN(xx, _style="color:green")
def zfunc(xx, rr_id): xx = xx[:10] if rr_id % 2 == 0: return SPAN(xx, _style="color:blue") else: return SPAN(xx, _style="color:brown")
def sql2table( tbl, db, tbl_query=None, order_by=None, rows_on_page=13, caller="index", csv=False, pagi=False, links=[], hlinks=[], fld_links={}, fld_skip=[ 0, ], fld_length=15, page_d={}, show_thead=True, ): def stop_button(): return A('!', _title='stop', _role='button', _style="background-color:lightgray;color:black;") if not tbl in db.tables: return f"unknown tbl: {tbl}" if tbl_query is None: tbl_query = db[tbl].id > 0 if tbl_query and not isinstance(tbl_query, pydal.objects.Query): return f"bad tbl_query! tbl: {tbl}" if order_by is None: order_by = ~db[tbl].id try: pg = int(page_d.get("page", 1)) except (ValueError, TypeError): pg = 1 table_items = len(db(tbl_query).select()) if rows_on_page > table_items: rows_on_page = table_items if table_items == 0: show_thead = False max_pages, rem = divmod(table_items, rows_on_page) if table_items else (0, 0) if rem: max_pages += 1 limitby = ((pg - 1) * rows_on_page, pg * rows_on_page) if not pagi: rows_on_page = table_items limitby = (0, table_items) rows = db(tbl_query).select(orderby=order_by, limitby=limitby) ij_start = -len(links) ff = [f for f in db[tbl].fields] hh = [db[tbl][f].label for f in ff] def h_func(x, jj): if jj < 0: if len(hlinks) >= -jj: return hlinks[len(hlinks) + jj] return "act" if jj in fld_skip: return '' if not x is None and isinstance(x, str) and len(x) > fld_length: x = x[:fld_length] + '...' return f"{x}" def r_func(x, ii, r, t, f_nm): if ii < 0: if len(links) >= -ii: return links[len(links) + ii](t, r.id) return "act" if ii in fld_skip: return '' if ii in fld_links: return fld_links[ii](t, x, r.id) if f_nm in fld_links: return fld_links[f_nm](t, x, r.id) if not x is None and isinstance(x, str) and len(x) > fld_length: x = x[:fld_length] + '~' return f"{x}" return DIV( SPAN("table_name: ", ), SPAN(f"{tbl}", _style="color:red"), SPAN(f"; {table_items} rows, {rows_on_page} rows_on_page, {pg} page "), DIV( # <div> SPAN( A( "prev", _role="button", _href=URL( caller, vars=dict(page=pg - 1 if pg > 1 else pg), ), ) if pg > 1 else stop_button(), A( "next", _role="button", _href=URL( caller, vars=dict(page=pg + 1 if pg < max_pages else pg), ), ) if pg < max_pages else stop_button(), ) if pagi else "", SPAN( A( "csv", _role="button", _title="table to csv file", _href=URL( "some_func", vars=dict(t_=tbl, c="a"), ), ), A( "xls", _role="button", _title="table to xls file", _href=URL( "some_func", vars=dict(t_=tbl, c="b"), ), ), ) if csv else "", ), # </div> TABLE( THEAD( TR(*[ TD(H6(h_func(hh[j], j))) for j in range(ij_start, len(hh)) ])) if show_thead else "", TBODY(*[ TR(*[ TD(r_func(row[ff[i]], i, row, tbl, ff[i])) for i in range(ij_start, len(ff)) ]) for row in rows ]), ), )
def produce( self, table, vars, errors, readonly, noncreate, deletable, classes=None, class_inner_exceptions=None, kwargs=None, ): self.classes.update(classes or {}) self.class_inner_exceptions.update(class_inner_exceptions or {}) kwargs = kwargs if kwargs else {} form_method = "POST" form_action = request.url form_enctype = "multipart/form-data" form = FORM( _method=form_method, _action=form_action, _enctype=form_enctype, **kwargs ) controls = Param( labels=dict(), widgets=dict(), comments=dict(), hidden_widgets=dict(), placeholders=dict(), titles=dict(), errors=dict(), begin=XML(form.xml().split("</form>")[0]), submit="", delete="", end=XML("</form>"), ) json_controls = dict( form_fields=[], form_values=dict(), form_buttons=[], form_method=form_method, form_action=form_action, form_enctype=form_enctype, **kwargs ) class_label = self.classes["label"] class_outer = self.classes["outer"] class_inner = self.classes["inner"] class_error = self.classes["error"] class_info = self.classes["info"] for field in table: # Reset the json control fields. field_attributes = dict() field_value = None field_name = field.name field_type = field.type field_comment = field.comment if field.comment else "" field_label = field.label input_id = "%s_%s" % (field.tablename, field.name) value = vars.get( field.name, field.default() if callable(field.default) else field.default, ) error = errors.get(field.name) field_class = "type-" + field.type.split()[0].replace(":", "-") placeholder = ( field._placeholder if "_placeholder" in field.__dict__ else None ) title = field._title if "_title" in field.__dict__ else None field_disabled = False # only diplay field if readable or writable if not field.readable and not field.writable: continue # if this is a reaonly field only show readable fields if readonly: if not field.readable: continue # if this is an create form (unkown id) then only show writable fields. Some if an edit form was made from a list of fields and noncreate=True elif not vars.get("id") and noncreate: if not field.writable: continue # ignore blob fields if field.type == "blob": # never display blobs (mistake?) continue # ignore fields of type id its value is equal to None if field.type == "id" and value is None: field.writable = False continue # if the form is readonly or this is an id type field, display it as readonly if readonly or not field.writable or field.type == "id": if field.type == "boolean": field_value = value field_type = "checkbox" control = INPUT( _type=field_type, _id=input_id, _name=field_name, _value="ON", _disabled="", _checked=value, _title=title, ) else: field_value = ( field.represent and field.represent(value) or value or "" ) field_type = "represent" control = DIV(field_value) field_disabled = True # if we have a widget for the field use it elif field.widget: control = field.widget(table, value) # Grab the custom widget attributes. field_attributes = control.attributes field_type = "widget" field_value = value # else pick the proper default widget elif field.type == "text": field_value = value or "" field_type = "text" control = TEXTAREA( field_value, _id=input_id, _name=field_name, _placeholder=placeholder, _title=title, ) elif field.type == "date": field_value = value field_type = "date" control = INPUT( _value=field_value, _type="date", _id=input_id, _name=field_name, _placeholder=placeholder, _title=title, ) elif field.type == "datetime": helpervalue = str(value) helpervalue = helpervalue.replace(" ", "T") field_value = helpervalue field_type = "datetime-local" control = INPUT( _value=helpervalue, _type="datetime-local", _id=input_id, _name=field_name, _placeholder=placeholder, _title=title, ) elif field.type == "time": field_value = value field_type = "time" control = INPUT( _value=value, _type="time", _id=input_id, _name=field_name, _placeholder=placeholder, _title=title, ) elif field.type == "boolean": field_value = value field_type = "checkbox" field_attributes["_value"] = "ON" control = INPUT( _type="checkbox", _id=input_id, _name=field_name, _value=field_attributes["_value"], _checked=value, _title=title, ) elif field.type == "upload": control = DIV() if value and not error: download_div = DIV() download_div.append( LABEL( "Currently: ", ) ) if getattr(field, "download_url", None): url = field.download_url(value) else: url = "#" download_div.append(A(" download ", _href=url)) # Set the download url. field_attributes["_download_url"] = url # Set the flag determining whether the file is an image. field_attributes["_is_image"] = (url != "#") and Form.is_image( value ) delete_checkbox_name = "_delete_" + field_name download_div.append( INPUT( _type="checkbox", _value="ON", _name=delete_checkbox_name, _title=title, ) ) download_div.append(" (check to remove)") delete_field_attributes = dict() delete_field_attributes["_label"] = "Remove" delete_field_attributes["_value"] = "ON" delete_field_attributes["_type"] = "checkbox" delete_field_attributes["_name"] = delete_checkbox_name json_controls["form_fields"] += [delete_field_attributes] json_controls["form_values"][delete_checkbox_name] = None control.append(download_div) control.append(LABEL("Change: ")) control.append(INPUT(_type="file", _id=input_id, _name=field_name)) field_value = None field_type = "file" elif get_options(field.requires) is not None and field.writable == True: multiple = field.type.startswith("list:") value = list(map(str, value if isinstance(value, list) else [value])) field_options = [ [k, v, (not k is None and k in value)] for k, v in get_options(field.requires) ] option_tags = [ OPTION(v, _value=k, _selected=_selected) for (k, v, _selected) in field_options ] control = SELECT( *option_tags, _id=input_id, _name=field_name, _multiple=multiple, _title=title ) field_value = value field_type = "options" field_attributes["_multiple"] = multiple field_attributes["_options"] = field_options else: field_type = "password" if field.type == "password" else "text" if field.type.startswith("list:"): value = json.dumps(value or []) field_value = None if field.type == "password" else value field_autocomplete = "off" if field_type == "password" else "on" control = INPUT( _type=field_type, _id=input_id, _name=field_name, _value=field_value, _class=field_class, _placeholder=placeholder, _title=title, _autocomplete=field_autocomplete, ) field_attributes["_autocomplete"] = field_autocomplete key = control.name.rstrip("/") if key == "input": key += "[type=%s]" % (control["_type"] or "text") control["_class"] = ( control.attributes.get("_class", "") + " " + self.classes.get(key, "") ).strip() # Set the form controls. controls["labels"][field_name] = field_label controls["widgets"][field_name] = control controls["comments"][field_name] = field_comment controls["titles"][field_name] = title controls["placeholders"][field_name] = placeholder # Set the remain json field attributes. field_attributes["_title"] = title field_attributes["_label"] = field_label field_attributes["_comment"] = field_comment field_attributes["_id"] = input_id field_attributes["_class"] = field_class field_attributes["_name"] = field_name field_attributes["_type"] = field_type field_attributes["_placeholder"] = placeholder field_attributes["_error"] = error field_attributes["_disabled"] = field_disabled # Add to the json controls. json_controls["form_fields"] += [field_attributes] json_controls["form_values"][field_name] = field_value if error: controls["errors"][field.name] = error if field.type == "boolean": form.append( DIV( SPAN(control, _class=class_inner), LABEL( " " + field.label, _for=input_id, _class=class_label, _style="display: inline !important", ), P(error, _class=class_error) if error else "", P(field.comment or "", _class=class_info), _class=class_outer, ) ) else: form.append( DIV( LABEL(field.label, _for=input_id, _class=class_label), DIV( control, _class=self.class_inner_exceptions.get( control.name, class_inner ), ), P(error, _class=class_error) if error else "", P(field.comment or "", _class=class_info), _class=class_outer, ) ) if vars.get("id"): form.append(INPUT(_name="id", _value=vars["id"], _hidden=True)) if deletable: deletable_record_attributes = dict() deletable_field_name = "_delete" deletable_field_type = "checkbox" # Set the deletable json field attributes. deletable_record_attributes["_label"] = " check to delete" deletable_record_attributes["_name"] = deletable_field_name deletable_record_attributes["_type"] = deletable_field_type deletable_record_attributes["_class"] = self.classes["input[type=checkbox]"] deletable_record_attributes["_value"] = "ON" # Add to the json controls. json_controls["form_fields"] += [deletable_record_attributes] json_controls["form_values"][deletable_field_name] = None controls["delete"] = INPUT( _type=deletable_field_type, _value=deletable_record_attributes["_value"], _name=deletable_field_name, _class=self.classes["input[type=checkbox]"], ) form.append( DIV( SPAN( controls["delete"], _class=class_inner, _stye="vertical-align: middle;", ), P( deletable_record_attributes["_label"], _class="help", _style="display: inline !important", ), _class=class_outer, ) ) submit_button_attributes = dict() submit_button_field_type = "submit" # Set the deletable json field attributes. submit_button_attributes["_label"] = "Submit" submit_button_attributes["_type"] = submit_button_field_type submit_button_attributes["_class"] = self.classes["input[type=submit]"] # Add to the json controls. json_controls["form_buttons"] += [submit_button_attributes] controls["submit"] = INPUT( _type=submit_button_field_type, _value="Submit", _class=self.classes["input[type=submit]"], ) submit = DIV( DIV( controls["submit"], _class=class_inner, ), _class=class_outer, ) form.append(submit) return dict(form=form, controls=controls, json_controls=json_controls)
def p4wupload_file(): t_id = dict(request.query).get("id_", "0") # if t_id != '0': # flash.set(f"deleted id={t_id}", sanitize=True) if not os.path.isdir(UPLOAD_FOLDER): return f"bad upload path: {UPLOAD_FOLDER}" messages = [] tbl = "uploaded_files" upload_field = "image" upload_form = Form( [ Field(upload_field, "upload", label="", requires=IS_NOT_EMPTY(),), Field("remark", default="mycomment"), ], formstyle=FormStyleDefault, ) if upload_form.accepted and hasattr(request, "files"): bottle_class = request.files.get(upload_field, None) if bottle_class: orig_fnm = bottle_class.raw_filename orig_fnm_content = bottle_class.file.read() uniq_file_name = get_unique_name(orig_fnm,) data2file(orig_fnm_content, os.path.join(UPLOAD_FOLDER, uniq_file_name)) row = dict( orig_file_name=orig_fnm, uniq_file_name=uniq_file_name, remark=upload_form.vars["remark"], ) if db[tbl].insert(**db[tbl]._filter_fields(row)): db.commit() elif upload_form.errors: messages.append(f"upload_form has errors: {upload_form.errors}") hlinks = ["save", "del"] links = [ lambda tx, r_id: A( f"save:[{r_id}]", _title="save file to disk", _href=URL(f"p4wdownload_file", vars=dict(t_=tx, id_=r_id)), ), lambda tx, r_id: A( f"del:[{r_id}]", _title="run p4wdelete_file", _href=URL(f"p4wdelete_file", vars=dict(t_=tx, id_=r_id)), ), ] fld_links = { # 'id': lambda tx, xx, r_id: A( # f'save[{r_id}]', # _title='save file to disk', # _href=URL(f"p4wdownload_file", vars=dict(t_=tx, x_=xx, id_=r_id)), # ), "time": lambda tx, xx, r_id: SPAN(xx.strftime(DATE_FORMAT), _style="color:red"), } mygrid = sql2table( tbl, db, links=links, hlinks=hlinks, fld_links=fld_links, rows_on_page=2, caller="p4wupload_file", page_d=dict(request.query), ) return dict(messages=messages, upload_form=upload_form, mygrid=mygrid)