Esempio n. 1
0
    def __init__(self,
                 ident=None,
                 per_page=_None,
                 on_page=_None,
                 qs_prefix='',
                 class_='datagrid',
                 **kwargs):
        self._ident = ident
        self.hah = HTMLAttributes(kwargs)
        self.hah.id = self.ident
        self.hah.class_ += class_
        self.filtered_cols = OrderedDict()
        self.subtotal_cols = OrderedDict()
        self.order_by = []
        self.qs_prefix = qs_prefix
        self.user_warnings = []
        self.search_value = None
        self._record_count = None
        self._records = None
        self._page_totals = None
        self._grand_totals = None
        if self.hide_excel_link is True:
            warnings.warn(
                "Hide excel link is deprecated, you should just override allowed_export_targets instead",  # noqa
                DeprecationWarning)
        if self.allowed_export_targets is None:
            self.allowed_export_targets = {}
            # If the grid doesn't define any export targets
            # lets setup the export targets for xls and xlsx if we have the requirement
            if xlwt is not None:
                self.allowed_export_targets['xls'] = XLS
            if xlsxwriter is not None:
                self.allowed_export_targets['xlsx'] = XLSX
        self.set_renderers()
        self.export_to = None
        # when session feature is enabled, key is the unique string
        #   used to distinguish grids. Initially set to a random
        #   string, but will be set to the session key in args
        self.session_key = randchars(12)
        # at times, different grids may be made to share a session
        self.foreign_session_loaded = False

        self.per_page = per_page if per_page is not _None else self.__class__.per_page
        self.on_page = on_page if on_page is not _None else self.__class__.on_page

        self.columns = []
        self.key_column_map = {}

        self._init_columns()
        self.post_init()
Esempio n. 2
0
def grouper(records, *fields):
    grouped_records = OrderedDict()

    def setup_grouping(record, *fields):
        location = []
        for field in fields:
            location.append(record[field])
        save_at_location(record, location)

    def save_at_location(record, location):
        at = grouped_records
        final_kpos = len(location)-1
        for kpos, key in enumerate(location):
            if kpos != final_kpos:
                if key not in at:
                    at[key] = OrderedDict()
                at = at[key]
            else:
                if key not in at:
                    at[key] = []
                at[key].append(record)

    for record in records:
        setup_grouping(record, *fields)
    return grouped_records
Esempio n. 3
0
 def todict(self, showinactive=False):
     if showinactive:
         return self._data
     d = OrderedDict()
     for k, v in self.iteritems():
         d[k] = v
     return d
    def test_ordereddict(self):
        o = OrderedDict((('a', 'a'), ('b', 'b')))

        # add a bunch of random junk so that a passing test is less likely to be accidentally valid
        for value in range(random.randint(100, 1000)):
            o[value] = value

        o['c'] = 'c'

        for i, v in enumerate(o.values()):
            if i == 0:
                assert v == 'a'
            elif i == 1:
                assert v == 'b'

        assert o.values()[-1] == 'c'
Esempio n. 5
0
    def test_ordereddict(self):
        o = OrderedDict((('a', 'a'), ('b', 'b')))

        # add a bunch of random junk so that a passing test is less likely to be accidentally valid
        for value in range(random.randint(100, 1000)):
            o[value] = value

        o['c'] = 'c'

        for i, v in enumerate(o.values()):
            if i == 0:
                assert v == 'a'
            elif i == 1:
                assert v == 'b'

        assert o.values()[-1] == 'c'
Esempio n. 6
0
 def init_columns(self):
     d = OrderedDict()
     d['label'] = SheetPortraitColumn(sheet=self, record=self.records[0])
     d['component'] = SheetPortraitColumn(sheet=self, record=self.records[0])
     d['income'] = SheetPortraitColumn(sheet=self, record=self.records[0])
     d['expense'] = SheetPortraitColumn(sheet=self, record=self.records[0])
     d['net'] = SheetPortraitColumn(sheet=self, record=self.records[0])
     return d
    def test_ordereddict_pickle(self):
        o = OrderedDict((('a', 'a'), ('b', 'b')))

        # add a bunch of random junk so that a passing test is less likely to be accidentally valid
        for value in range(random.randint(100, 1000)):
            o[value] = value

        o['c'] = 'c'

        ostr = pickle.dumps(o, pickle.HIGHEST_PROTOCOL)
        o = pickle.loads(ostr)

        for i, v in enumerate(o.values()):
            if i == 0:
                assert v == 'a'
            elif i == 1:
                assert v == 'b'

        assert o.values()[-1] == 'c'
Esempio n. 8
0
    def test_ordereddict_pickle(self):
        o = OrderedDict((('a', 'a'), ('b', 'b')))

        # add a bunch of random junk so that a passing test is less likely to be accidentally valid
        for value in range(random.randint(100, 1000)):
            o[value] = value

        o['c'] = 'c'

        ostr = pickle.dumps(o, pickle.HIGHEST_PROTOCOL)
        o = pickle.loads(ostr)

        for i, v in enumerate(o.values()):
            if i == 0:
                assert v == 'a'
            elif i == 1:
                assert v == 'b'

        assert o.values()[-1] == 'c'
Esempio n. 9
0
 def expandkeys(self):
     retval = OrderedDict()
     for k in self.keys():
         v = getattr(self, k)
         if isinstance(v, QuickSettings):
             for vk, vv in v.expandkeys().iteritems():
                 new_key = '%s.%s' % (k, vk)
                 retval[new_key] = vv
         else:
             retval[k] = v
     return retval
Esempio n. 10
0
 def save_at_location(record, location):
     at = grouped_records
     final_kpos = len(location)-1
     for kpos, key in enumerate(location):
         if kpos != final_kpos:
             if key not in at:
                 at[key] = OrderedDict()
             at = at[key]
         else:
             if key not in at:
                 at[key] = []
             at[key].append(record)
Esempio n. 11
0
def gatherobjs(dotpath, filter, reloadmod=False):
    """
        like visitmods(), but instead of just importing the module it gathers
        objects out of the module, passing them to filter to determine if they
        should be kept or not.
    """
    def getkey(app, pname):
        if not pname:
            return 'appstack.%s' % dotpath
        return 'compstack.%s.%s' % (pname, dotpath)

    collected = OrderedDict()

    def process_module(module, app, pname, package):
        modkey = getkey(app, pname)
        for k, v in six.iteritems(vars(module)):
            if filter(k, v):
                modattrs = collected.setdefault(modkey, OrderedDict())
                modattrs.setdefault(k, v)

    visitmods(dotpath, call_with_mod=process_module, reloadmod=reloadmod)
    return collected
Esempio n. 12
0
    def __init__(self, ident=None, per_page=_None, on_page=_None, qs_prefix='', class_='datagrid',
                 **kwargs):
        self._ident = ident
        self.hah = HTMLAttributes(kwargs)
        self.hah.id = self.ident
        self.hah.class_ += class_
        self.filtered_cols = OrderedDict()
        self.subtotal_cols = OrderedDict()
        self.order_by = []
        self.qs_prefix = qs_prefix
        self.user_warnings = []
        self._record_count = None
        self._records = None
        self._page_totals = None
        self._grand_totals = None
        if self.hide_excel_link is True:
            warnings.warn(
                "Hide excel link is deprecated, you should just override allowed_export_targets instead", # noqa
                DeprecationWarning
            )
        if self.allowed_export_targets is None:
            self.allowed_export_targets = {}
            # If the grid doesn't define any export targets
            # lets setup the export targets for xls and xlsx if we have the requirement
            if xlwt is not None:
                self.allowed_export_targets['xls'] = XLS
            if xlsxwriter is not None:
                self.allowed_export_targets['xlsx'] = XLSX
        self.set_renderers()
        self.export_to = None
        # when session feature is enabled, key is the unique string
        #   used to distinguish grids. Initially set to a random
        #   string, but will be set to the session key in args
        self.session_key = randchars(12)
        # at times, different grids may be made to share a session
        self.foreign_session_loaded = False

        self.per_page = per_page if per_page is not _None else self.__class__.per_page
        self.on_page = on_page if on_page is not _None else self.__class__.on_page

        self.columns = []
        self.key_column_map = {}

        def subtotal_function_map(v):
            # subtotals default to the simplest expression (sum). avg is also an option, or you
            #   can assign a string or expression (string using column labels would probably
            #   work best at this stage)
            if v is True or v == 'sum':
                return sum_
            elif v == 'avg':
                return avg_
            return v

        for col in self.__cls_cols__:
            new_col = col.new_instance(self)
            self.columns.append(new_col)
            self.key_column_map[new_col.key] = new_col
            if new_col.filter is not None:
                self.filtered_cols[new_col.key] = new_col
            if new_col.has_subtotal is not False and new_col.has_subtotal is not None:
                self.subtotal_cols[new_col.key] = (
                    subtotal_function_map(new_col.has_subtotal),
                    new_col
                )

        self.post_init()
Esempio n. 13
0
class BaseGrid(six.with_metaclass(_DeclarativeMeta, object)):
    __cls_cols__ = ()
    identifier = None
    sorter_on = True
    pager_on = True
    per_page = 50
    on_page = 1
    hide_controls_box = False
    hide_excel_link = False
    # enables keyed session store of grid arguments
    session_on = False
    # enables page/grand subtotals: none|page|grand|all
    subtotals = 'none'
    manager = None
    allowed_export_targets = None
    # Enables single-search feature, where one search value is applied to every supporting
    # filter at once
    enable_search = False

    # List of joins to bring the query together for all columns. May have just the join object,
    # or also conditions
    # e.g. [Blog], ([Blog.category], ), or [(Blog, Blog.active == sa.true())]
    # note: relationship attributes must be referenced within tuples, due to SQLAlchemy magic
    query_joins = None
    query_outer_joins = None
    # Filter parameter(s) tuple to be used on the query
    # note: relationship attributes must be referenced within tuples, due to SQLAlchemy magic
    query_filter = None
    # Parameter(s) tuple to be passed to order_by if sort options are not set on the grid
    # note: relationship attributes must be referenced within tuples, due to SQLAlchemy magic
    query_default_sort = None

    # Will ask for confirmation before exporting more than this many records.
    # Set to None to disable this check
    unconfirmed_export_limit = 10000

    def __init__(self,
                 ident=None,
                 per_page=_None,
                 on_page=_None,
                 qs_prefix='',
                 class_='datagrid',
                 **kwargs):
        self._ident = ident
        self.hah = HTMLAttributes(kwargs)
        self.hah.id = self.ident
        self.hah.class_ += class_
        self.filtered_cols = OrderedDict()
        self.subtotal_cols = OrderedDict()
        self.order_by = []
        self.qs_prefix = qs_prefix
        self.user_warnings = []
        self.search_value = None
        self._record_count = None
        self._records = None
        self._page_totals = None
        self._grand_totals = None
        if self.hide_excel_link is True:
            warnings.warn(
                "Hide excel link is deprecated, you should just override allowed_export_targets instead",  # noqa
                DeprecationWarning)
        if self.allowed_export_targets is None:
            self.allowed_export_targets = {}
            # If the grid doesn't define any export targets
            # lets setup the export targets for xls and xlsx if we have the requirement
            if xlwt is not None:
                self.allowed_export_targets['xls'] = XLS
            if xlsxwriter is not None:
                self.allowed_export_targets['xlsx'] = XLSX
        self.set_renderers()
        self.export_to = None
        # when session feature is enabled, key is the unique string
        #   used to distinguish grids. Initially set to a random
        #   string, but will be set to the session key in args
        self.session_key = randchars(12)
        # at times, different grids may be made to share a session
        self.foreign_session_loaded = False

        self.per_page = per_page if per_page is not _None else self.__class__.per_page
        self.on_page = on_page if on_page is not _None else self.__class__.on_page

        self.columns = []
        self.key_column_map = {}

        self._init_columns()
        self.post_init()

    def _init_columns(self):
        for col in self.__cls_cols__:
            new_col = col.new_instance(self)
            self.columns.append(new_col)
            self.key_column_map[new_col.key] = new_col
            if new_col.filter is not None:
                self.filtered_cols[new_col.key] = new_col
            if new_col.has_subtotal is not False and new_col.has_subtotal is not None:
                self.subtotal_cols[new_col.key] = (subtotal_function_map(
                    new_col.has_subtotal), new_col)

    def post_init(self):
        """Provided for subclasses to run post-initialization customizations"""
        pass

    def before_query_hook(self):
        """ Just a hook to give subclasses a chance to change things before executing the query """
        pass

    def build(self):
        self.apply_qs_args()
        self.before_query_hook()
        # this will force the query to execute.  We used to wait to evaluate this but it ended
        # up causing AttributeErrors to be hidden when the grid was used in Jinja.
        # Calling build is now preferred over calling .apply_qs_args() and then .html()
        self.record_count

    def column(self, ident):
        if isinstance(ident, six.string_types):
            return self.key_column_map[ident]
        return self.columns[ident]

    def iter_columns(self, render_type):
        for col in self.columns:
            if col.visible and render_type in col.render_in:
                yield col

    def can_search(self):
        # enable_search will turn the feature on/off, but don't enable it if none of the filters
        # support it
        return self.enable_search and len(
            self.search_expression_generators) > 0

    @property
    def search_expression_generators(self):
        # See FilterBase.get_search_expr
        # Should return a tuple of callables, each taking a single argument (the search value).
        # We filter out None here so as to disregard filters that don't support the search feature.
        def check_expression_generator(expr_gen):
            if expr_gen is not None and not callable(expr_gen):
                raise Exception(
                    'bad filter search expression: {} is not callable'.format(
                        str(expr_gen)))
            return expr_gen is not None

        return tuple(
            filter(check_expression_generator, [
                col.filter.get_search_expr()
                for col in self.filtered_cols.values()
            ]))

    def set_renderers(self):
        self.html = HTML(self)
        for key, value in self.allowed_export_targets.items():
            setattr(self, key, value(self))

    def set_filter(self, key, op, value):
        self.clear_record_cache()
        self.filtered_cols[key].filter.set(op, value)

    def set_sort(self, *args):
        self.clear_record_cache()
        self.order_by = []

        for key in args:
            if not key:
                continue
            flag_desc = False
            if key.startswith('-'):
                flag_desc = True
                key = key[1:]
            if key in self.key_column_map and self.key_column_map[key].can_sort:
                self.order_by.append((key, flag_desc))
            elif not self.foreign_session_loaded:
                self.user_warnings.append(
                    _('''can't sort on invalid key "{key}"''', key=key))

    def set_paging(self, per_page, on_page):
        self.clear_record_cache()
        self.per_page = per_page
        self.on_page = on_page

    def clear_record_cache(self):
        self._record_count = None
        self._records = None

    @property
    def ident(self):
        return self._ident \
            or self.identifier \
            or case_cw2us(self.__class__.__name__)

    @property
    def has_filters(self):
        for col in six.itervalues(self.filtered_cols):
            if col.filter.is_active:
                return True
        return self.search_value is not None

    @property
    def has_sort(self):
        return bool(self.order_by)

    @property
    def record_count(self):
        if self._record_count is None:
            query = self.build_query(for_count=True)
            t0 = time.perf_counter()
            self._record_count = query.count()
            t1 = time.perf_counter()
            log.debug('Count query ran in {} seconds'.format(t1 - t0))
        return self._record_count

    @property
    def records(self):
        if self._records is None:
            query = self.build_query()
            t0 = time.perf_counter()
            self._records = query.all()
            t1 = time.perf_counter()
            log.debug('Data query ran in {} seconds'.format(t1 - t0))
        return self._records

    def _totals_col_results(self, page_totals_only):
        SUB = self.build_query(for_count=(not page_totals_only)).subquery()

        cols = []
        for colname, coltuple in six.iteritems(self.subtotal_cols):
            sa_aggregate_func, colobj = coltuple

            # column may have a label. If it does, use it
            if isinstance(colobj.expr, sasql.expression._Label):
                aggregate_this = sasql.text(colobj.key)
            elif colobj.expr is None:
                aggregate_this = sasql.literal_column(colobj.key)
            else:
                aggregate_this = colobj.expr

            # sa_aggregate_func could be an expression, or a callable. If it is callable, give it
            #   the column
            labeled_aggregate_col = None
            if callable(sa_aggregate_func):
                labeled_aggregate_col = sa_aggregate_func(
                    aggregate_this).label(colname)
            elif isinstance(sa_aggregate_func, six.string_types):
                labeled_aggregate_col = sasql.literal_column(
                    sa_aggregate_func).label(colname)
            else:
                labeled_aggregate_col = sa_aggregate_func.label(colname)
            cols.append(labeled_aggregate_col)

        t0 = time.perf_counter()
        result = self.manager.sa_query(*cols).select_entity_from(SUB).first()
        t1 = time.perf_counter()
        log.debug('Totals query ran in {} seconds'.format(t1 - t0))

        return result

    @property
    def page_totals(self):
        if self._page_totals is None:
            self._page_totals = self._totals_col_results(page_totals_only=True)
        return self._page_totals

    @property
    def grand_totals(self):
        if self._grand_totals is None:
            self._grand_totals = self._totals_col_results(
                page_totals_only=False)
        return self._grand_totals

    @property
    def page_count(self):
        if self.per_page is None:
            return 1
        return max(0, self.record_count - 1) // self.per_page + 1

    def build_query(self, for_count=False):
        log.debug(str(self))

        has_filters = self.has_filters
        query = self.query_base(self.has_sort, has_filters)
        query = self.query_prep(query, self.has_sort or for_count, has_filters)

        if has_filters:
            query = self.query_filters(query)
        else:
            log.debug('No filters')

        if for_count:
            return query

        query = self.query_sort(query)
        if self.pager_on:
            query = self.query_paging(query)

        return query

    def set_records(self, records):
        self._record_count = len(records)
        self._records = records

    def query_base(self, has_sort, has_filters):
        cols = [col.expr for col in self.columns if col.expr is not None]
        query = self.manager.sa_query(*cols)

        for join_terms in (tolist(self.query_joins) or tuple()):
            query = query.join(*tolist(join_terms))

        for join_terms in (tolist(self.query_outer_joins) or tuple()):
            query = query.outerjoin(*tolist(join_terms))

        if self.query_filter:
            query = query.filter(*tolist(self.query_filter))

        if not has_sort and self.query_default_sort is not None:
            query = query.order_by(*tolist(self.query_default_sort))

        return query

    def query_prep(self, query, has_sort, has_filters):
        return query

    def query_filters(self, query):
        filter_display = []
        if self.search_value is not None:
            query = self.apply_search(query, self.search_value)

        for col in six.itervalues(self.filtered_cols):
            if col.filter.is_active:
                filter_display.append('{}: {}'.format(col.key,
                                                      str(col.filter)))
                query = col.filter.apply(query)
        if filter_display:
            log.debug(';'.join(filter_display))
        else:
            log.debug('No filters')
        return query

    def apply_search(self, query, value):
        # We depend on the filters to know what to do with the search value, and then OR the
        # expressions together for our query
        return query.filter(
            sa.or_(*filter(lambda item: item is not None, (
                expr(value) for expr in self.search_expression_generators))))

    def query_paging(self, query):
        if self.on_page and self.per_page:
            offset = (self.on_page - 1) * self.per_page
            query = query.offset(offset).limit(self.per_page)
            log.debug('Page {}; {} per page'.format(self.on_page,
                                                    self.per_page))
        return query

    def query_sort(self, query):
        redundant = []
        sort_display = []
        for key, flag_desc in self.order_by:
            if key in self.key_column_map:
                col = self.key_column_map[key]
                # remove any redundant names, whichever comes first is what we will keep
                if col.key in redundant:
                    continue
                else:
                    sort_display.append(col.key)
                    redundant.append(col.key)
                query = col.apply_sort(query, flag_desc)
        if sort_display:
            log.debug(','.join(sort_display))
        else:
            log.debug('No sorts')

        return query

    def args_have_op(self, args):
        # any of the grid's query string args can be used to
        #   override the session behavior (except export_to)
        r = re.compile(self.qs_prefix + r'(op\(.*\))')
        return any(r.match(a) for a in args.keys())

    def args_have_session_override(self, args):
        r = re.compile(self.qs_prefix + 'session_override')
        return any(r.match(a) for a in args.keys())

    def args_have_page(self, args):
        r = re.compile(self.qs_prefix + '(onpage|perpage)')
        return any(r.match(a) for a in args.keys())

    def args_have_sort(self, args):
        r = re.compile(self.qs_prefix + '(sort[1-3])')
        return any(r.match(a) for a in args.keys())

    def apply_qs_args(self, add_user_warnings=True):
        args = MultiDict(self.manager.request_args())
        if 'search' in args and self.can_search():
            self.search_value = args['search'].strip()

        # args are pulled first from the request. If the session feature
        #   is enabled and the request doesn't include grid-related args,
        #   check for either the session key or a default set in the
        #   session args store
        if self.session_on:
            # if session key is in request, set the unique key
            self.session_key = args.get(self.prefix_qs_arg_key('session_key'),
                                        self.session_key)
            session_override = self.args_have_session_override(args)
            # apply arg indicates that filtering/paging/sorting form was submitted
            apply = self.prefix_qs_arg_key('apply') in args
            args.pop(self.prefix_qs_arg_key('apply'), None)
            if (not self.args_have_op(args) and not apply) or session_override:
                session_args = self.get_session_store(args, session_override)
                # override paging if it exists in the query
                if self.args_have_page(args):
                    session_args['onpage'] = args.get('onpage')
                    session_args['perpage'] = args.get('perpage')
                # override sorting if it exists in the query
                if self.args_have_sort(args):
                    session_args['sort1'] = args.get('sort1')
                    session_args['sort2'] = args.get('sort2')
                    session_args['sort3'] = args.get('sort3')
                # flag a foreign session if loading from another grid's session
                grid_key = self.__class__.__name__
                if session_args.get('datagrid', grid_key) != grid_key:
                    self.foreign_session_loaded = True
                args = session_args

            req_args = self.manager.request_args()
            if self.prefix_qs_arg_key('export_to') in req_args:
                args[self.prefix_qs_arg_key('export_to')] = \
                    req_args[self.prefix_qs_arg_key('export_to')]
            self.save_session_store(args)

        # filtering (make sure this is above paging otherwise self.page_count
        # used in the paging section below won't work)
        self._apply_filtering(args)

        # paging
        self._apply_paging(args)

        # sorting
        self._apply_sorting(args)

        if add_user_warnings:
            for msg in self.user_warnings:
                self.manager.flash_message('warning', msg)

    def _apply_filtering(self, args):
        for col in six.itervalues(self.filtered_cols):
            filter = col.filter
            filter_op_qsk = self.prefix_qs_arg_key('op({0})'.format(col.key))
            filter_v1_qsk = self.prefix_qs_arg_key('v1({0})'.format(col.key))
            filter_v2_qsk = self.prefix_qs_arg_key('v2({0})'.format(col.key))

            filter_op_value = args.get(filter_op_qsk, None)

            if filter._default_op:
                filter.set(None, None, None)

            if filter_op_value is not None:
                if filter.receives_list:
                    v1 = args.getlist(filter_v1_qsk)
                    v2 = args.getlist(filter_v2_qsk)
                else:
                    v1 = args.get(filter_v1_qsk, None)
                    v2 = args.get(filter_v2_qsk, None)

                try:
                    filter.set(
                        filter_op_value,
                        v1,
                        v2,
                    )
                except Invalid as e:
                    invalid_msg = filter.format_invalid(e, col)
                    self.user_warnings.append(invalid_msg)

    def _apply_paging(self, args):
        pp_qsk = self.prefix_qs_arg_key('perpage')
        if pp_qsk in args:
            per_page = self.apply_validator(fev.Int, args[pp_qsk], pp_qsk)
            if per_page is None or per_page < 1:
                per_page = 1
            self.per_page = per_page

        op_qsk = self.prefix_qs_arg_key('onpage')
        if op_qsk in args:
            on_page = self.apply_validator(fev.Int, args[op_qsk], op_qsk)
            if on_page is None or on_page < 1:
                on_page = 1
            if on_page > self.page_count:
                on_page = self.page_count
            self.on_page = on_page

    def _apply_sorting(self, args):
        sort_qs_keys = [
            self.prefix_qs_arg_key('sort1'),
            self.prefix_qs_arg_key('sort2'),
            self.prefix_qs_arg_key('sort3'),
        ]
        sort_qs_values = [
            args[sort_qsk] for sort_qsk in sort_qs_keys if sort_qsk in args
        ]
        if sort_qs_values:
            self.set_sort(*sort_qs_values)

        # handle other file formats
        export_qsk = self.prefix_qs_arg_key('export_to')
        self.set_export_to(args.get(export_qsk, None))

    def prefix_qs_arg_key(self, key):
        return '{0}{1}'.format(self.qs_prefix, key)

    def apply_validator(self, validator, value, qs_arg_key):
        try:
            return validator.to_python(value)
        except Invalid:
            invalid_msg = _('"{arg}" grid argument invalid, ignoring',
                            arg=qs_arg_key)
            self.user_warnings.append(invalid_msg)
            return None

    def set_export_to(self, to):
        if to in self.allowed_export_targets:
            self.export_to = to

    def export_as_response(self, wb=None, sheet_name=None):
        if not self.export_to:
            raise ValueError('No export format set')
        exporter = getattr(self, self.export_to)
        if self.export_to in ['xls', 'xlsx']:
            return exporter.as_response(wb, sheet_name)
        return exporter.as_response()

    def get_session_store(self, args, session_override=False):
        # check args for a session key. If the key is present,
        #   look it up in the session and use the saved args
        #   (if they have been saved under that key). If not,
        #   look up the class name for a default arg store.
        web_session = self.manager.web_session()
        if 'dgsessions' not in web_session:
            return args
        dgsessions = web_session['dgsessions']
        stored_args = None
        # if dgreset is in args, store the session key if present
        #   and then pass back the incoming args
        reset = self.prefix_qs_arg_key('dgreset') in args

        # session is stored as a JSON-serialized list of tuples, which we can turn into MultiDict
        session_key = '_{0}'.format(self.__class__.__name__)
        if args.get(self.prefix_qs_arg_key('session_key'), None):
            if dgsessions.get(self.session_key, None):
                session_key = self.session_key
        stored_args_json = dgsessions.get(session_key, '[]')
        if isinstance(stored_args_json, MultiDict):
            stored_args_json = json.dumps(
                list(stored_args_json.items(multi=True)))
        elif isinstance(stored_args_json, dict):
            stored_args_json = json.dumps(list(stored_args_json.items()))
        stored_args = MultiDict(json.loads(stored_args_json))

        if stored_args and session_override:
            for arg, value in args.items():
                stored_args[arg] = value
            stored_args.pop('session_override')
        return stored_args if (stored_args and not reset) else args

    def save_session_store(self, args):
        # save the args in the session under the session key
        #   and also as the default args for this grid
        web_session = self.manager.web_session()
        if 'dgsessions' not in web_session:
            web_session['dgsessions'] = dict()
        dgsessions = web_session['dgsessions']
        # work with a copy here
        args = MultiDict(args)
        # remove keys that should not be stored
        args.pop(self.prefix_qs_arg_key('export_to'), None)
        args.pop(self.prefix_qs_arg_key('dgreset'), None)
        args['datagrid'] = self.__class__.__name__
        # serialize the args so we can enforce the correct MultiDict type on the other side
        args_json = json.dumps(list(args.items(multi=True)))
        # save in store under grid default and session key
        dgsessions[self.session_key] = args_json
        dgsessions['_{0}'.format(self.__class__.__name__)] = args_json

        # some frameworks/sessions need these changes manually persisted
        self.manager.persist_web_session()

    def __repr__(self):
        return '<Grid "{0}">'.format(self.__class__.__name__)
Esempio n. 14
0
 def process_module(module, app, pname, package):
     modkey = getkey(app, pname)
     for k, v in six.iteritems(vars(module)):
         if filter(k, v):
             modattrs = collected.setdefault(modkey, OrderedDict())
             modattrs.setdefault(k, v)