diff --git a/CHANGELOG.md b/CHANGELOG.md index 5baed07..7390f4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. +## [0.5.3] + +- Enhanced Field prepare flow +- Add traversing for group_by +- Allowed tests to run specific tests instead of the whole suit +- Enhanced templates structure for easier override/customization + ## [0.5.2] - Enhanced Time Series Plot total HighChart by accenting the categories diff --git a/docs/source/conf.py b/docs/source/conf.py index 56fc773..7362ae4 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -24,7 +24,7 @@ master_doc = 'index' # The full version, including alpha/beta/rc tags -release = '0.5.2' +release = '0.5.3' # -- General configuration --------------------------------------------------- diff --git a/runtests.py b/runtests.py index 001be57..5f753e1 100644 --- a/runtests.py +++ b/runtests.py @@ -2,15 +2,28 @@ import os import sys +import argparse import django from django.conf import settings from django.test.utils import get_runner if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Run the Django Slick Reporting test suite.") + parser.add_argument( + 'modules', nargs='*', metavar='module', + help='Optional path(s) to test modules; e.g. "i18n" or ' + '"i18n.tests.TranslationTests.test_lazy_objects".', + ) + options = parser.parse_args() + + options.modules = [os.path.normpath(labels) for labels in options.modules] + + os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.test_settings' django.setup() TestRunner = get_runner(settings) test_runner = TestRunner() - failures = test_runner.run_tests(["tests"]) + failures = test_runner.run_tests(options.modules) + # failures = test_runner.run_tests(["tests"]) sys.exit(bool(failures)) diff --git a/slick_reporting/__init__.py b/slick_reporting/__init__.py index efe36c1..1caadbd 100644 --- a/slick_reporting/__init__.py +++ b/slick_reporting/__init__.py @@ -1,6 +1,6 @@ default_app_config = 'slick_reporting.apps.ReportAppConfig' -VERSION = (0, 5, 2) +VERSION = (0, 5, 3) -__version__ = '0.5.2' +__version__ = '0.5.3' diff --git a/slick_reporting/fields.py b/slick_reporting/fields.py index 17731a9..fab5031 100644 --- a/slick_reporting/fields.py +++ b/slick_reporting/fields.py @@ -110,6 +110,21 @@ def apply_aggregation(self, queryset, group_by=''): queryset = queryset.aggregate(annotation) return queryset + def init_preparation(self, q_filters=None, kwargs_filters=None, **kwargs): + """ + Called by the generator to preparet he calculation of this field + it's requirements + :param q_filters: + :param kwargs_filters: + :param kwargs: + :return: + """ + kwargs_filters = kwargs_filters or {} + + dep_values = self._prepare_dependencies(q_filters, kwargs_filters.copy()) + + debit_results, credit_results = self.prepare(q_filters, kwargs_filters, **kwargs) + self._cache = debit_results, credit_results, dep_values + def prepare(self, q_filters=None, kwargs_filters=None, **kwargs): """ This is the first hook where you can customize the calculation away from the Django Query aggregation method @@ -122,10 +137,6 @@ def prepare(self, q_filters=None, kwargs_filters=None, **kwargs): :param kwargs: :return: """ - kwargs_filters = kwargs_filters or {} - - dep_values = self._prepare_dependencies(q_filters, kwargs_filters.copy()) - queryset = self.get_queryset() if q_filters: queryset = queryset.filter(*q_filters) @@ -148,8 +159,7 @@ def prepare(self, q_filters=None, kwargs_filters=None, **kwargs): credit_results = self.apply_aggregation(queryset, self.group_by) - self._cache = debit_results, credit_results, dep_values - return debit_results, credit_results, dep_values + return debit_results, credit_results def get_queryset(self): queryset = self.report_model.objects @@ -167,15 +177,16 @@ def _prepare_dependencies(self, q_filters=None, extra_filters=None, ): for dep_class in self._require_classes: dep = dep_class(self.plus_side_q, self.minus_side_q, self.report_model, date_field=self.date_field, group_by=self.group_by) - values[dep.name] = {'results': dep.prepare(q_filters, extra_filters), + values[dep.name] = {'results': dep.init_preparation(q_filters, extra_filters), 'instance': dep} return values - def resolve(self, current_obj): + def resolve(self, current_obj, current_row=None): ''' Reponsible for getting the exact data from the prepared value :param cached: the returned data from prepare - :param current_obj: + :param current_obj: he value of group by id + :param current_row: the row in iteration :return: a solid number or value ''' cached = self._cache diff --git a/slick_reporting/generator.py b/slick_reporting/generator.py index 20de4d6..e259e6a 100644 --- a/slick_reporting/generator.py +++ b/slick_reporting/generator.py @@ -193,14 +193,20 @@ def __init__(self, report_model=None, main_queryset=None, start_date=None, end_d # todo validate columns is not empty (if no time series / cross tab) if self.group_by: + group_by_split = self.group_by.split('__') + search_field = group_by_split[0] try: - self.group_by_field = [x for x in self.report_model._meta.fields if x.name == self.group_by][0] + self.group_by_field = [x for x in self.report_model._meta.fields if x.name == search_field][0] + except IndexError: raise ImproperlyConfigured( f'Can not find group_by field:{self.group_by} in report_model {self.report_model} ') + self.focus_field_as_key = self.group_by_field + if '__' not in self.group_by: + self.group_by_field_attname = self.group_by_field.attname + else: + self.group_by_field_attname = self.group_by - self.focus_field_as_key = self.group_by - self.group_by_field_attname = self.group_by_field.attname else: self.focus_field_as_key = None self.group_by_field_attname = None @@ -236,17 +242,16 @@ def __init__(self, report_model=None, main_queryset=None, start_date=None, end_d else: self.main_queryset = self._apply_queryset_options(main_queryset) - if type(self.group_by_field) is ForeignKey: - ids = self.main_queryset.values_list(self.group_by_field.attname).distinct() + if type(self.group_by_field) is ForeignKey and '__' not in self.group_by: + ids = self.main_queryset.values_list(self.group_by_field_attname).distinct() self.main_queryset = self.group_by_field.related_model.objects.filter(pk__in=ids).values() else: - self.main_queryset = self.main_queryset.distinct().values(self.group_by_field.attname) + self.main_queryset = self.main_queryset.distinct().values(self.group_by_field_attname) else: if self.time_series_pattern: self.main_queryset = [{}] else: self.main_queryset = self._apply_queryset_options(main_queryset, self.get_database_columns()) - self._prepare_report_dependencies() def _apply_queryset_options(self, query, fields=None): @@ -322,8 +327,7 @@ def _prepare_report_dependencies(self): if window == 'crosstab': q_filters = self._construct_crosstab_filter(col_data) - # print(f'preparing {report_class} for {window}') - report_class.prepare(q_filters, date_filter) + report_class.init_preparation(q_filters, date_filter) self.report_fields_classes[name] = report_class def _get_record_data(self, obj, columns): @@ -359,7 +363,7 @@ def _get_record_data(self, obj, columns): computation_class = self.report_fields_classes[name] except KeyError: continue - value = computation_class.resolve(group_by_val) + value = computation_class.resolve(group_by_val, data) if self.swap_sign: value = -value data[name] = value @@ -402,7 +406,7 @@ def check_columns(cls, columns, group_by, report_model, ): """ group_by_model = None if group_by: - group_by_field = [x for x in report_model._meta.fields if x.name == group_by][0] + group_by_field = [x for x in report_model._meta.fields if x.name == group_by.split('__')[0]][0] if group_by_field.is_relation: group_by_model = group_by_field.related_model else: @@ -450,7 +454,7 @@ def check_columns(cls, columns, group_by, report_model, ): } else: # A database field - model_to_use = group_by_model if group_by else report_model + model_to_use = group_by_model if group_by and '__' not in group_by else report_model try: if '__' in col: # A traversing link order__client__email diff --git a/slick_reporting/templates/slick_reporting/base.html b/slick_reporting/templates/slick_reporting/base.html index b3bcd0c..8c60515 100644 --- a/slick_reporting/templates/slick_reporting/base.html +++ b/slick_reporting/templates/slick_reporting/base.html @@ -42,9 +42,9 @@ integrity="JnbsSLBmv2/R0fUmF2XYIcAEMPHEAO51Gitn9IjL4l89uFTIgtLF1+jqIqqd9FSk" crossorigin="anonymous"> - - - + {% block js_script %} {% endblock %} diff --git a/slick_reporting/templates/slick_reporting/simple_report.html b/slick_reporting/templates/slick_reporting/simple_report.html index 345196b..0e1b018 100644 --- a/slick_reporting/templates/slick_reporting/simple_report.html +++ b/slick_reporting/templates/slick_reporting/simple_report.html @@ -1,5 +1,5 @@ {% extends 'slick_reporting/base.html' %} -{% load crispy_forms_tags i18n slick_reporting_tags %} +{% load crispy_forms_tags i18n slick_reporting_tags static %} {% block content %} @@ -53,6 +53,9 @@

Results

{% endblock %} {% block js_script %} + + +