Example #1
0
 def choices(self):
     if self.pk:
         model = get_model_from_path_string(self.report.root_model.model_class(), self.path)
         return self.get_choices(model, self.field)
 def choices(self):
     if self.pk:
         model = get_model_from_path_string(
             self.report.root_model.model_class(), self.path)
         return self.get_choices(model, self.field)
Example #3
0
 def test_get_model_from_path_string(self):
     result = get_model_from_path_string(Restaurant, 'waiter__name')
     self.assertEqual(result, Waiter)
Example #4
0
        except ValueError, e:
            message += "Value Error: {0!s}. Something may be wrong with the report's filters. For example it may be expecting a number but received a character.".format(e)
            return [], message

    # Display Values
    display_field_paths = []
    property_list = {}
    custom_list = {}
    display_totals = {}
    def append_display_total(display_totals, display_field, display_field_key):
        if display_field.total:
            display_totals[display_field_key] = {'val': Decimal('0.00')}


    for i, display_field in enumerate(report.displayfield_set.all()):
        model = get_model_from_path_string(model_class, display_field.path)
        if user.has_perm(model._meta.app_label + '.change_' + model._meta.module_name) \
        or user.has_perm(model._meta.app_label + '.view_' + model._meta.module_name) \
        or not model:
            # TODO: clean this up a bit
            display_field_key = display_field.path + display_field.field
            if '[property]' in display_field.field_verbose:
                property_list[i] = display_field_key 
                append_display_total(display_totals, display_field, display_field_key)
            elif '[custom' in display_field.field_verbose:
                custom_list[i] = display_field_key 
                append_display_total(display_totals, display_field, display_field_key)
            elif display_field.aggregate == "Avg":
                display_field_key += '__avg'
                display_field_paths += [display_field_key]
                append_display_total(display_totals, display_field, display_field_key)
Example #5
0
def report_to_list(report, user, preview=False, queryset=None):
    """ Create list from a report with all data filtering
    preview: Return only first 50
    objects: Provide objects for list, instead of running filters
    Returns list, message in case of issues
    """
    message = ''
    model_class = report.root_model.model_class()
    if queryset != None:
        objects = report.add_aggregates(queryset)
    else:
        try:
            objects, query_message = report.get_query()
            message += query_message
        except exceptions.ValidationError as e:
            message += 'Validation Error: {0!s}. Something may be wrong with the report\'s filters.'.format(e)
            return [], message
        except ValueError as e:
            message += 'Value Error: {0!s}. Something may be wrong with the report\'s filters. For example it may be expecting a number but received a character.'.format(e)
            return [], message

    # Display Values
    display_field_paths = []
    property_list = {}
    custom_list = {}
    display_totals = {}

    def append_display_total(display_totals, display_field, display_field_key):
        if display_field.total:
            display_totals[display_field_key] = {'val': Decimal('0.00')}

    for i, display_field in enumerate(report.displayfield_set.all()):
        model = get_model_from_path_string(model_class, display_field.path)
        if user.has_perm(model._meta.app_label + '.change_' + model._meta.module_name) \
        or user.has_perm(model._meta.app_label + '.view_' + model._meta.module_name) \
        or not model:
            # TODO: clean this up a bit
            display_field_key = display_field.path + display_field.field
            if '[property]' in display_field.field_verbose:
                property_list[i] = display_field_key
                append_display_total(display_totals, display_field, display_field_key)
            elif '[custom' in display_field.field_verbose:
                custom_list[i] = display_field_key
                append_display_total(display_totals, display_field, display_field_key)
            elif display_field.aggregate == "Avg":
                display_field_key += '__avg'
                display_field_paths += [display_field_key]
                append_display_total(display_totals, display_field, display_field_key)
            elif display_field.aggregate == "Max":
                display_field_key += '__max'
                display_field_paths += [display_field_key]
                append_display_total(display_totals, display_field, display_field_key)
            elif display_field.aggregate == "Min":
                display_field_key += '__min'
                display_field_paths += [display_field_key]
                append_display_total(display_totals, display_field, display_field_key)
            elif display_field.aggregate == "Count":
                display_field_key += '__count'
                display_field_paths += [display_field_key]
                append_display_total(display_totals, display_field, display_field_key)
            elif display_field.aggregate == "Sum":
                display_field_key += '__sum'
                display_field_paths += [display_field_key]
                append_display_total(display_totals, display_field, display_field_key)
            else:
                display_field_paths += [display_field_key]
                append_display_total(display_totals, display_field, display_field_key)
        else:
            message += "You don't have permission to " + display_field.name
    try:
        if user.has_perm(report.root_model.app_label + '.change_' + report.root_model.model) \
        or user.has_perm(report.root_model.app_label + '.view_' + report.root_model.model):

            def increment_total(display_field_key, display_totals, val):
                if display_field_key in display_totals:
                    # Booleans are Numbers - blah
                    if isinstance(val, Number) and not isinstance(val, bool):
                        # do decimal math for all numbers
                        display_totals[display_field_key]['val'] += Decimal(str(val))
                    else:
                        display_totals[display_field_key]['val'] += Decimal('1.00')

            # get pk for primary and m2m relations in order to retrieve objects
            # for adding properties to report rows
            display_field_paths.insert(0, 'pk')
            m2m_relations = []
            for position, property_path in property_list.iteritems():
                property_root = property_path.split('__')[0]
                root_class = report.root_model.model_class()
                property_root_class = getattr(root_class, property_root)
                if type(property_root_class) == ReverseManyRelatedObjectsDescriptor:
                    display_field_paths.insert(1, '%s__pk' % property_root)
                    m2m_relations.append(property_root)
            values_and_properties_list = []
            filtered_report_rows = []
            group = None
            for df in report.displayfield_set.all():
                if df.group:
                    group = df.path + df.field
                    break
            if group:
                filtered_report_rows = report.add_aggregates(objects.values_list(group))
            else:
                values_list = objects.values_list(*display_field_paths)

            if not group:
                for row in values_list:
                    row = list(row)
                    obj = report.root_model.model_class().objects.get(pk=row.pop(0))
                    #related_objects
                    remove_row = False
                    values_and_properties_list.append(row)
                    # filter properties (remove rows with excluded properties)
                    property_filters = report.filterfield_set.filter(
                        Q(field_verbose__contains='[property]') | Q(field_verbose__contains='[custom')
                        )
                    for property_filter in property_filters:
                        root_relation = property_filter.path.split('__')[0]
                        if root_relation in m2m_relations:
                            pk = row[0]
                            if pk is not None:
                                # a related object exists
                                m2m_obj = getattr(obj, root_relation).get(pk=pk)
                                val = reduce(getattr, [property_filter.field], m2m_obj)
                            else:
                                val = None
                        else:
                            if '[custom' in property_filter.field_verbose:
                                for relation in property_filter.path.split('__'):
                                    if hasattr(obj, root_relation):
                                        obj = getattr(obj, root_relation)
                                val = obj.get_custom_value(property_filter.field)
                            else:
                                val = reduce(getattr, (property_filter.path + property_filter.field).split('__'), obj)
                        if filter_property(property_filter, val):
                            remove_row = True
                            values_and_properties_list.pop()
                            break
                    if not remove_row:
                        # increment totals for fields
                        for i, field in enumerate(display_field_paths[1:]):
                            if field in display_totals.keys():
                                increment_total(field, display_totals, row[i])
                        for position, display_property in property_list.iteritems():
                            relations = display_property.split('__')
                            root_relation = relations[0]
                            if root_relation in m2m_relations:
                                pk = row.pop(0)
                                if pk is not None:
                                    # a related object exists
                                    m2m_obj = getattr(obj, root_relation).get(pk=pk)
                                    val = reduce(getattr, relations[1:], m2m_obj)
                                else:
                                    val = None
                            else:
                                try:  # Could error if a related field doesn't exist
                                    val = reduce(getattr, relations, obj)
                                except AttributeError:
                                    val = None
                            values_and_properties_list[-1].insert(position, val)
                            increment_total(display_property, display_totals, val)
                        for position, display_custom in custom_list.iteritems():
                            val = obj.get_custom_value(display_custom)
                            values_and_properties_list[-1].insert(position, val)
                            increment_total(display_custom, display_totals, val)
                        filtered_report_rows += [values_and_properties_list[-1]]
                    if preview and len(filtered_report_rows) == 50:
                        break
            sort_fields = report.displayfield_set.filter(sort__gt=0).order_by('-sort').\
                values_list('position', 'sort_reverse')
            for sort_field in sort_fields:
                try:
                    filtered_report_rows = sorted(
                        filtered_report_rows,
                        key=lambda x: sort_helper(x, sort_field[0] - 1),
                        reverse=sort_field[1]
                        )
                except TypeError:  # Sorry crappy way to determine if date is being sorted
                    filtered_report_rows = sorted(
                        filtered_report_rows,
                        key=lambda x: sort_helper(x, sort_field[0] - 1, date_field=True),
                        reverse=sort_field[1]
                        )
            values_and_properties_list = filtered_report_rows
        else:
            values_and_properties_list = []
            message = "Permission Denied on %s" % report.root_model.name

        # add choice list display and display field formatting
        choice_lists = {}
        display_formats = {}
        final_list = []
        for df in report.displayfield_set.all():
            if df.choices:
                df_choices = df.choices_dict
                # Insert blank and None as valid choices
                df_choices[''] = ''
                df_choices[None] = ''
                choice_lists.update({df.position: df_choices})
            if df.display_format:
                display_formats.update({df.position: df.display_format})

        for row in values_and_properties_list:
            # add display totals for grouped result sets
            # TODO: dry this up, duplicated logic in non-grouped total routine
            if group:
                # increment totals for fields
                for i, field in enumerate(display_field_paths[1:]):
                    if field in display_totals.keys():
                        increment_total(field, display_totals, row[i])
            row = list(row)
            for position, choice_list in choice_lists.iteritems():
                row[position - 1] = unicode(choice_list[row[position - 1]])
            for position, display_format in display_formats.iteritems():
                # convert value to be formatted into Decimal in order to apply
                # numeric formats
                try:
                    value = Decimal(row[position - 1])
                except:
                    value = row[position - 1]
                # Try to format the value, let it go without formatting for ValueErrors
                try:
                    row[position - 1] = display_format.string.format(value)
                except ValueError:
                    row[position - 1] = value
            final_list.append(row)
        values_and_properties_list = final_list

        if display_totals:
            display_totals_row = []

            fields_and_properties = list(display_field_paths[1:])
            for position, value in property_list.iteritems():
                fields_and_properties.insert(position, value)
            for i, field in enumerate(fields_and_properties):
                if field in display_totals.keys():
                    display_totals_row += [display_totals[field]['val']]
                else:
                    display_totals_row += ['']

            # add formatting to display totals
            for df in report.displayfield_set.all():
                if df.display_format:
                    try:
                        value = Decimal(display_totals_row[df.position - 1])
                    except:
                        value = display_totals_row[df.position - 1]
                    display_totals_row[df.position - 1] = df.display_format.string.\
                        format(value)

        if display_totals:
            values_and_properties_list = (
                values_and_properties_list + [
                    ['TOTALS'] + (len(fields_and_properties) - 1) * ['']
                    ] + [display_totals_row]
                )

    except exceptions.FieldError:
        message += "Field Error. If you are using the report builder then you found a bug!"
        message += "If you made this in admin, then you probably did something wrong."
        values_and_properties_list = None

    return values_and_properties_list, message