Coverage for lino/core/tablerequest.py : 48%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: UTF-8 -*- # Copyright 2009-2016 Luc Saffre # License: BSD (see file COPYING for details)
""" # from builtins import str
#~ if col.label: #~ return join_elems(col.label.split('\n'),sep=E.br) #~ return [unicode(col.name)]
"""A specialized :class:`ActionRequest <lino.core.requests.ActionRequest>` whose actor is a :class:`table <lino.core.tables.AbstractTable>`.
"""
# known_values = None
"""This will actually call the :meth:`get_data_iterator` and run the database query.
Automatically called when either :attr:`data_iterator` or :attr:`sliced_data_iterator` is accesed.
""" except Warning as e: #~ logger.info("20130809 Warning %s",e) self.no_data_text = six.text_type(e) self._data_iterator = [] except Exception as e: if not settings.SITE.catch_layout_exceptions: raise # Report this exception. But since such errors may occur # rather often and since exception loggers usually send an # email to the local system admin, make sure to log each # exception only once. self.no_data_text = six.text_type(e) w = WARNINGS_LOGGED.get(six.text_type(e)) if w is None: WARNINGS_LOGGED[six.text_type(e)] = True raise # logger.exception(e) self._data_iterator = []
# print 20150718, self._data_iterator self._data_iterator = tuple(self._data_iterator) if self.offset is not None: assert self.limit is not None num = self.get_total_count() max_pages = int(old_div(num, self.limit)) page = old_div(self.offset, self.limit) page = max_pages - page offset = self.limit * page # offset = max_offset - self.offset self._sliced_data_iterator = self._sliced_data_iterator[ offset:] self._sliced_data_iterator = self._sliced_data_iterator[ :self.limit] else: offset = self.offset if offset == -1: assert self.limit is not None num = self.get_total_count() page_num = int(old_div(num, self.limit)) offset = self.limit * page_num self._sliced_data_iterator = self._sliced_data_iterator[ offset:] :self.limit]
#~ logger.info("20120914 tables.get_data_iterator %s",self) #~ logger.info("20120914 tables.get_data_iterator %s",self.actor)
""" Calling `len()` on a QuerySet would execute the whole SELECT. See `/blog/2012/0124` """ return di.count() #~ if di is None: #~ raise Exception("data_iterator is None: %s" % self) return len(di) else: except TypeError: raise TypeError("{0} has no length".format(di))
return self.data_iterator.__iter__()
return self.data_iterator.__getitem__(i)
"""Parse the incoming HttpRequest and translate it into keyword arguments to be used by :meth:`setup`.
The `mt` url param is parsed only when needed. Usually it is not needed because the `master_class` is constant and known per actor. But there are exceptions:
- `master` is `ContentType`
- `master` is some abstract model
- `master` is not a subclass of Model, e.g. :class:`lino.modlib.polls.models.AnswersByResponse`, a virtual table which defines :meth:`get_row_by_pk <lino.core.actors.Actor.get_row_by_pk>`.
""" #~ logger.info("20120723 %s.parse_req()",self.actor) #~ rh = self.ah
raise Exception("20150216 not a type: %r" % master) master is ContentType or master._meta.abstract): mt = rqdata.get(constants.URL_PARAM_MASTER_TYPE) try: master = kw['master'] = ContentType.objects.get( pk=mt).model_class() except ContentType.DoesNotExist: pass # master is None
#~ print '20100406a', self.actor,URL_PARAM_MASTER_PK,"=",pk #~ if pk in ('', '-99999'): pk = None kw['master_instance'] = None else: raise ObjectDoesNotExist( "Invalid master key {0} for {1}".format( pk, self.actor))
# ~ print '20100212', self #, kw['master_instance'] #~ print '20100406b', self.actor,kw
exclude = dict() for f in self.ah.store.fields: if f.field: filterOption = rqdata.get( 'filter[%s_filterOption]' % f.field.name) if filterOption == 'empty': kw[f.field.name + "__isnull"] = True elif filterOption == 'notempty': kw[f.field.name + "__isnull"] = False else: filterValue = rqdata.get('filter[%s]' % f.field.name) if filterValue: if not filterOption: filterOption = 'contains' if filterOption == 'contains': kw[f.field.name + "__icontains"] = filterValue elif filterOption == 'doesnotcontain': exclude[f.field.name + "__icontains"] = filterValue else: print("unknown filterOption %r" % filterOption) if len(exclude): kw.update(exclude=exclude)
filter = json.loads(filter) kw['gridfilters'] = [constants.dict2kw(flt) for flt in filter]
#~ raise Exception("20120121 %s.parse_req(%s)" % (self,kw))
#~ kw.update(self.report.known_values) #~ for fieldname, default in self.report.known_values.items(): #~ v = request.REQUEST.get(fieldname,None) #~ if v is not None: #~ kw[fieldname] = v
kw.update(quick_search=quick_search)
sort_dir = rqdata.get(constants.URL_PARAM_SORTDIR, 'ASC') if sort_dir == 'DESC': sort = '-' + sort kw.update(order_by=[sort])
kw.update(offset=int(offset)) constants.URL_PARAM_LIMIT, self.actor.preview_limit) except ValueError: # Example: invalid literal for int() with base 10: # 'fdpkvcnrfdybhur' raise SuspiciousOperation("Invalid value for limit or offset")
quick_search=None, order_by=None, offset=None, limit=None, master=None, title=None, master_id=None, filter=None, gridfilters=None, exclude=None, extra=None, **kw):
#~ logger.info("20120519 %s.setup()",self)
# master might still be None
self.title = title
raise Exception("20150218 deprecated?") # assert master_instance is None # master_instance = self.master.objects.get(pk=master_id)
# Table.page_length is not a default value for ReportRequest.limit # For example CSVReportRequest wants all rows.
#~ logger.info("20120121 %s.setup() done",self)
# 20120519 : outbox.MyOutbox had no phantom record when called # from menu. When called by permalink it had. Because # get_create_kw was called before Actor.setup_request() which # sets the master_instance.
self.offset = offset
"""Returns a string representing this table request in reStructuredText markup.
""" stdout = sys.stdout sys.stdout = StringIO() self.table2rst(*args, **kw) rv = sys.stdout.getvalue() sys.stdout = stdout return rv
"""Print a reStructuredText representation of this table request to stdout.
""" settings.SITE.kernel.text_renderer.show_table(self, *args, **kwargs)
""" Return an HTML representation of this table request. """ t = xghtml.Table() self.dump2html(t, self.sliced_data_iterator, **kw) e = t.as_element() # print "20150822 table2xhtml", E.tostring(e) if header_level is not None: return E.div(E.h2(self.actor.label), e) return e
""" Render this table into an existing :class:`lino.utils.xmlgen.html.Table` instance.
""" ar = self tble.attrib.update(self.tableattrs) tble.attrib.setdefault('name', self.bound_action.full_name())
grid = ar.ah.list_layout.main columns = grid.columns fields, headers, cellwidths = ar.get_field_info(column_names) columns = fields #~ print 20130330, cellwidths
headers = [ x for x in grid.headers2html( self, columns, headers, **self.cellattrs)] sums = [fld.zero for fld in columns] #~ hr = tble.add_header_row(*headers,**self.cellattrs) if cellwidths: for i, td in enumerate(headers): td.attrib.update(width=six.text_type(cellwidths[i])) tble.head.append(xghtml.E.tr(*headers)) #~ print 20120623, ar.actor recno = 0 for obj in data_iterator: cells = ar.row2html(recno, columns, obj, sums, **self.cellattrs) if cells is not None: recno += 1 #~ ar.actor.apply_row_format(tr,recno) tble.body.append(xghtml.E.tr(*cells))
if recno == 0: tble.clear() tble.body.append(ar.no_data_text)
if not ar.actor.hide_sums: has_sum = False for i in sums: if i: has_sum = True break if has_sum: cells = ar.sums2html(columns, sums, **self.cellattrs) tble.body.append(xghtml.E.tr(*cells))
"""Return a tuple `(fields, headers, widths)` which expresses which columns, headers and widths the user wants for this request. If `self` has web request info, checks for GET parameters cn, cw and ch (coming from the grid widget). Also apply the tables's :meth:`override_column_headers <lino.core.actors.Actor.override_column_headers>` method.
"""
else: data = getrqdata(ar.request) columns = [ six.text_type(x) for x in data.getlist(constants.URL_PARAM_COLUMNS)] all_widths = data.getlist(constants.URL_PARAM_WIDTHS) hiddens = [(x == 'true') for x in data.getlist( constants.URL_PARAM_HIDDENS)] fields = [] widths = [] ah = ar.actor.get_handle() for i, cn in enumerate(columns): col = None for e in ah.list_layout.main.columns: if e.name == cn: col = e break if col is None: raise Exception("No column named %r in %s" % (cn, ar.ah.list_layout.main.columns)) if not hiddens[i]: fields.append(col) widths.append(int(all_widths[i])) else: from lino.core.layouts import ColumnsLayout ll = ColumnsLayout(column_names, datasource=ar.actor) lh = ll.get_layout_handle(settings.SITE.kernel.default_ui) columns = lh.main.columns columns = [e for e in columns if not e.hidden] else:
# render them so that babelfields in hidden_languages # get hidden: # e.value.get('hidden', False)]
for col in columns] #~ 20130415 widths = ["%d%%" % (col.width or col.preferred_width) for col in columns] #~ fields = [col.field._lino_atomizer for col in columns]
for i, e in enumerate(columns): header = oh.get(e.name, None) if header is not None: headers[i] = header #~ print 20120507, oh, headers
return getit() else:
has_numeric_value = False cells = [] for i, col in enumerate(columns): v = col.field._lino_atomizer.full_value_from_object(row, self) if v is None: td = E.td(**cellattrs) else: nv = col.value2num(v) if nv != 0: sums[i] += nv has_numeric_value = True td = col.value2html(self, v, **cellattrs) col.apply_cell_format(td) self.actor.apply_cell_format(self, row, col, recno, td) cells.append(td) if self.actor.hide_zero_rows and not has_numeric_value: return None return cells
"""Render the given `row` into a line of text, using the given list of `fields` and collecting sums into `sums`.
""" try: getter = sf.full_value_from_object v = getter(row, self) except Exception as e: raise Exception("20150218 %s: %s" % (sf, e)) # was used to find bug 20130422: yield "%s:\n%s" % (fld.field, e) continue else:
yield '' else: # # In case you want the field name in error message: # try: # sums[i] += fld.value2num(v) # except Exception as e: # raise e.__class__("%s %s" % (fld.field, e))
return [fld.sum2html(self, sums, i, **cellattrs) for i, fld in enumerate(columns)]
return self.title
"""Extends :meth:`lino.core.requests.ActorRequest.get_status`.
""" bp[constants.URL_PARAM_FILTER] = self.quick_search
sort = self.order_by[0] if sort.startswith('-'): sort = sort[1:] bp[constants.URL_PARAM_SORTDIR] = 'DESC' bp[constants.URL_PARAM_SORT] = sort
self.master_instance.__class__).pk # else: # logger.warning("20141205 %s %s", # self.master_instance, # ContentType) else: # if self.master is None: bp[constants.URL_PARAM_MASTER_PK] = self.master_instance
kw = dict() if self.master_instance is not None: kw.update(master_instance=obj2str(self.master_instance)) if self.filter is not None: kw.update(filter=repr(self.filter)) if self.known_values: kw.update(known_values=self.known_values) if self.requesting_panel: kw.update(requesting_panel=self.requesting_panel) u = self.get_user() if u is not None: kw.update(user=u.username) if False: # self.request: kw.update(request=format_request(self.request)) return "<%s %s(%s)>" % ( self.__class__.__name__, self.bound_action.full_name(), kw)
|