From f61808d0dec97b90046eff56f669e359e9dc20e9 Mon Sep 17 00:00:00 2001 From: Ramez Issac <ramez@rasystems.io> Date: Thu, 11 Jun 2020 16:34:26 +0200 Subject: [PATCH 1/6] Fix is_summable for Crosstab Send computation_field to frontend --- slick_reporting/generator.py | 9 +++------ slick_reporting/views.py | 1 + tests/test_generator.py | 10 ++++++++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/slick_reporting/generator.py b/slick_reporting/generator.py index c746fc8..690b61c 100644 --- a/slick_reporting/generator.py +++ b/slick_reporting/generator.py @@ -382,7 +382,7 @@ def _parse(self): magic_field_class = None if attr: - #todo Add testing here + # todo Add testing here col_data = {'name': col, 'verbose_name': getattr(attr, 'verbose_name', col), # 'type': 'method', @@ -547,20 +547,17 @@ def get_crosstab_parsed_columns(self): ids_length = len(ids) - 1 for counter, id in enumerate(ids): for col in report_columns: - # try: magic_field_class = field_registry.get_field_by_name(col) - # except: - # magic_field_class = None - output_cols.append({ 'name': f'{col}CT{id}', + 'original_name': col, 'verbose_name': self.get_crosstab_field_verbose_name(magic_field_class, self.crosstab_model, id), 'ref': magic_field_class, 'id': id, 'model': self.crosstab_model, 'is_reminder': counter == ids_length, 'source': 'magic_field' if magic_field_class else '', - 'summable': magic_field_class.is_summable, + 'is_summable': magic_field_class.is_summable, }) return output_cols diff --git a/slick_reporting/views.py b/slick_reporting/views.py index 37fce22..4158709 100644 --- a/slick_reporting/views.py +++ b/slick_reporting/views.py @@ -146,6 +146,7 @@ def get_columns_data(self, columns): for col in columns: data.append({ 'name': col['name'], + 'computation_field': col.get('original_name', ''), 'verbose_name': col['verbose_name'], 'visible': col.get('visible', True), 'type': col.get('type', 'text'), diff --git a/tests/test_generator.py b/tests/test_generator.py index b44ec11..5e11069 100644 --- a/tests/test_generator.py +++ b/tests/test_generator.py @@ -34,6 +34,16 @@ def test_get_crosstab_columns(self): columns = report.get_list_display_columns() self.assertEqual(len(columns), 8, [x['name'] for x in columns]) + def test_get_crosstab_parsed_columns(self): + """ + Test important attributes are passed . + :return: + """ + report = CrosstabOnClient(crosstab_ids=[self.client1.pk], crosstab_compute_reminder=False) + columns = report.get_crosstab_parsed_columns() + for col in columns: + self.assertTrue(col.get('is_summable', False)) + class GeneratorReportStructureTest(TestCase): def test_time_series_columns_inclusion(self): From 29c12b4ccf4939039fe11ecec9f669f5071427fa Mon Sep 17 00:00:00 2001 From: Ramez Issac <ramez@rasystems.io> Date: Thu, 11 Jun 2020 18:17:10 +0200 Subject: [PATCH 2/6] Fix is_summable test --- tests/test_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_generator.py b/tests/test_generator.py index 5e11069..6d53537 100644 --- a/tests/test_generator.py +++ b/tests/test_generator.py @@ -42,7 +42,7 @@ def test_get_crosstab_parsed_columns(self): report = CrosstabOnClient(crosstab_ids=[self.client1.pk], crosstab_compute_reminder=False) columns = report.get_crosstab_parsed_columns() for col in columns: - self.assertTrue(col.get('is_summable', False)) + self.assertTrue('is_summable' in col.keys(), col) class GeneratorReportStructureTest(TestCase): From ded0b5e1c3f02e31036e020650008194d145ae38 Mon Sep 17 00:00:00 2001 From: Ramez Issac <ramez@rasystems.io> Date: Sat, 13 Jun 2020 11:48:13 +0200 Subject: [PATCH 3/6] Simplify the default datetime ReportForm date are datetime instead of split datetime --- slick_reporting/app_settings.py | 4 +- slick_reporting/form_factory.py | 37 ++++++++----------- slick_reporting/helpers.py | 5 +++ .../slick_reporting/simple_report.html | 6 +-- .../templates/slick_reporting/table.html | 2 +- 5 files changed, 26 insertions(+), 28 deletions(-) diff --git a/slick_reporting/app_settings.py b/slick_reporting/app_settings.py index a23adce..0ea4c08 100644 --- a/slick_reporting/app_settings.py +++ b/slick_reporting/app_settings.py @@ -7,12 +7,12 @@ def get_first_of_this_year(): d = datetime.datetime.today() - return datetime.datetime(d.year, 1, 1, 0, 0, 0, 0, pytz.timezone(settings.TIME_ZONE)) + return datetime.datetime(d.year, 1, 1, 0, 0) def get_end_of_this_year(): d = datetime.datetime.today() - return datetime.datetime(d.year + 1, 1, 1, 0, 0, 0, 0, pytz.timezone(settings.TIME_ZONE)) + return datetime.datetime(d.year + 1, 1, 1, 0, 0) SLICK_REPORTING_DEFAULT_START_DATE = getattr(settings, '', lazy(get_first_of_this_year, datetime.datetime)()) diff --git a/slick_reporting/form_factory.py b/slick_reporting/form_factory.py index b826be4..088cb81 100644 --- a/slick_reporting/form_factory.py +++ b/slick_reporting/form_factory.py @@ -58,8 +58,9 @@ def get_crispy_helper(self, foreign_keys_map=None, crosstab_model=None, **kwargs helper.form_class = 'form-horizontal' helper.label_class = 'col-sm-2 col-md-2 col-lg-2' helper.field_class = 'col-sm-10 col-md-10 col-lg-10' - helper.form_tag = False + helper.form_tag = True helper.disable_csrf = True + helper.render_unmentioned_fields = True foreign_keys_map = foreign_keys_map or self.foreign_keys @@ -69,21 +70,12 @@ def get_crispy_helper(self, foreign_keys_map=None, crosstab_model=None, **kwargs Field('start_date'), css_class='col-sm-6'), Column( Field('end_date'), css_class='col-sm-6'), - css_class='raReportDateRange'), Div(css_class="mt-20", style='margin-top:20px') ) - # if crosstab_model: - # entry_point.append(Row( - # Div('matrix_entities', css_class='col-sm-9'), - # Div('matrix_show_other', css_class='col-sm-3') - # , css_class='matrixField') - # ) - for k in foreign_keys_map: - if k[:-3] != crosstab_model: - helper.layout.fields[1].append(Field(k)) + helper.layout.fields[1].append(Field(k)) return helper @@ -105,17 +97,17 @@ def report_form_factory(model, fkeys_filter_func=None, foreign_key_widget_func=N fkeys_list = [] fields = OrderedDict() - fields['start_date'] = forms.SplitDateTimeField(required=False, label=_('From date'), - initial=app_settings.SLICK_REPORTING_DEFAULT_START_DATE, - # widget=RaBootstrapDateTime(), - input_date_formats=['%Y-%m-%d', '%Y-%m-%d'], - input_time_formats=['%H:%M', '%H:%M:%S'], - ) + fields['start_date'] = forms.DateTimeField(required=False, label=_('From date'), + initial=app_settings.SLICK_REPORTING_DEFAULT_START_DATE, + widget=forms.DateTimeInput(format='%m/%d/%Y %H:%M', + attrs={'autocomplete': "off"}), + ) - fields['end_date'] = forms.SplitDateTimeField(required=False, label=_('To date'), - initial=app_settings.SLICK_REPORTING_DEFAULT_END_DATE, - # widget=RaBootstrapDateTime(), - ) + fields['end_date'] = forms.DateTimeField(required=False, label=_('To date'), + initial=app_settings.SLICK_REPORTING_DEFAULT_END_DATE, + widget=forms.DateTimeInput(format='%m/%d/%Y %H:%M', + attrs={'autocomplete': "off"}) + ) for name, f_field in fkeys_map.items(): fkeys_list.append(name) @@ -132,6 +124,7 @@ def report_form_factory(model, fkeys_filter_func=None, foreign_key_widget_func=N {"base_fields": fields, '_fkeys': fkeys_list, 'foreign_keys': fkeys_map, - 'crosstab_model': crosstab_model + 'crosstab_model': crosstab_model, + 'crosstab_display_compute_reminder': display_compute_reminder, }) return new_form diff --git a/slick_reporting/helpers.py b/slick_reporting/helpers.py index 5d33e59..45263fb 100644 --- a/slick_reporting/helpers.py +++ b/slick_reporting/helpers.py @@ -13,6 +13,11 @@ def get_calculation_annotation(calculation_field, calculation_method): def get_foreign_keys(model): + """ + Scans a model and return an Ordered Dictionary with the foreign keys found + :param model: the model to scan + :return: Ordered Dict + """ from django.db import models fields = model._meta.get_fields() fkeys = OrderedDict() diff --git a/slick_reporting/templates/slick_reporting/simple_report.html b/slick_reporting/templates/slick_reporting/simple_report.html index 76d5437..415ed4e 100644 --- a/slick_reporting/templates/slick_reporting/simple_report.html +++ b/slick_reporting/templates/slick_reporting/simple_report.html @@ -35,9 +35,9 @@ <h4 class="py-5">Results</h4> height="100"></canvas> {% endif %} </div> - <div class="report-table"></div> - {% include 'slick_reporting/table.html' with table=report_data %} - + <div class="report-table"> + {% include 'slick_reporting/table.html' with table=report_data %} + </div> </div> </div> diff --git a/slick_reporting/templates/slick_reporting/table.html b/slick_reporting/templates/slick_reporting/table.html index 34e0ef9..4c902ea 100644 --- a/slick_reporting/templates/slick_reporting/table.html +++ b/slick_reporting/templates/slick_reporting/table.html @@ -1,5 +1,5 @@ {% load slick_reporting_tags %} -<table class="table .reportTable "> +<table class="table reportTable {{ table_class|default:'' }} "> <thead> <tr> {% for column in table.columns %} From 7c473d718c0bd587aaa2fa73d33553da1e165467 Mon Sep 17 00:00:00 2001 From: Ramez Issac <ramez@rasystems.io> Date: Thu, 18 Jun 2020 09:03:07 +0200 Subject: [PATCH 4/6] Better Form organization for Crosstab models Add metadata for crosstab --- slick_reporting/form_factory.py | 9 ++++++++- slick_reporting/views.py | 6 +++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/slick_reporting/form_factory.py b/slick_reporting/form_factory.py index 088cb81..c804baa 100644 --- a/slick_reporting/form_factory.py +++ b/slick_reporting/form_factory.py @@ -74,8 +74,15 @@ def get_crispy_helper(self, foreign_keys_map=None, crosstab_model=None, **kwargs Div(css_class="mt-20", style='margin-top:20px') ) + # first add the crosstab model and its display reimder then the rest of the fields + if self.crosstab_model: + helper.layout.fields[1].append(Field(self.crosstab_key_name)) + if self.crosstab_display_compute_reminder: + helper.layout.fields[1].append(Field('crosstab_compute_reminder')) + for k in foreign_keys_map: - helper.layout.fields[1].append(Field(k)) + if k != self.crosstab_key_name: + helper.layout.fields[1].append(Field(k)) return helper diff --git a/slick_reporting/views.py b/slick_reporting/views.py index 4158709..1c76fdc 100644 --- a/slick_reporting/views.py +++ b/slick_reporting/views.py @@ -179,10 +179,14 @@ def get_metadata(self, generator): :return: """ time_series_columns = generator.get_time_series_parsed_columns() + crosstab_columns = generator.get_crosstab_parsed_columns() metadata = { 'time_series_pattern': self.time_series_pattern, 'time_series_column_names': [x['name'] for x in time_series_columns], - 'time_series_column_verbose_names': [x['verbose_name'] for x in time_series_columns] + 'time_series_column_verbose_names': [x['verbose_name'] for x in time_series_columns], + 'crosstab_model': self.crosstab_model, + 'crosstab_column_names': [x['name'] for x in crosstab_columns], + 'crosstab_column_verbose_names': [x['verbose_name'] for x in crosstab_columns], } return metadata From 0964a88f0a779a812df9240e55797c8c6e431a74 Mon Sep 17 00:00:00 2001 From: Ramez Issac <ramez@rasystems.io> Date: Fri, 24 Jul 2020 12:51:38 +0200 Subject: [PATCH 5/6] Rename quan to quantity --- slick_reporting/fields.py | 2 +- slick_reporting/form_factory.py | 11 +++++++++++ tests/report_generators.py | 8 ++++---- tests/test_generator.py | 2 +- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/slick_reporting/fields.py b/slick_reporting/fields.py index c1c03ce..6585d2d 100644 --- a/slick_reporting/fields.py +++ b/slick_reporting/fields.py @@ -379,7 +379,7 @@ class FirstBalanceQTYReportField(FirstBalanceField): class BalanceQTYReportField(BaseReportField): - name = '__balance_quan__' + name = '__balance_quantity__' verbose_name = _('Cumulative QTY') calculation_field = 'quantity' requires = ['__fb_quan__'] diff --git a/slick_reporting/form_factory.py b/slick_reporting/form_factory.py index c804baa..e285721 100644 --- a/slick_reporting/form_factory.py +++ b/slick_reporting/form_factory.py @@ -94,6 +94,17 @@ def _default_foreign_key_widget(f_field): def report_form_factory(model, fkeys_filter_func=None, foreign_key_widget_func=None, crosstab_model=None, display_compute_reminder=True, **kwargs): + """ + Create a Reprot Form based on the report_model passed + + :param model: + :param fkeys_filter_func: + :param foreign_key_widget_func: + :param crosstab_model: + :param display_compute_reminder: + :param kwargs: + :return: + """ foreign_key_widget_func = foreign_key_widget_func or _default_foreign_key_widget fkeys_filter_func = fkeys_filter_func or (lambda x: x) diff --git a/tests/report_generators.py b/tests/report_generators.py index cdb1627..21b3ca3 100644 --- a/tests/report_generators.py +++ b/tests/report_generators.py @@ -57,7 +57,7 @@ class ProductTotalSales(ReportGenerator): report_model = SimpleSales date_field = 'doc_date' group_by = 'product' - columns = ['slug', 'name', '__balance__', '__balance_quan__'] + columns = ['slug', 'name', '__balance__', '__balance_quantity__'] class ClientList(ReportGenerator): @@ -81,7 +81,7 @@ class ProductClientSales(ReportGenerator): header_report = ClientList group_by = 'product' - columns = ['slug', 'name', '__balance_quan__', '__balance__'] + columns = ['slug', 'name', '__balance_quantity__', '__balance__'] class ProductSalesMonthlySeries(ReportGenerator): @@ -94,13 +94,13 @@ class ProductSalesMonthlySeries(ReportGenerator): 'group_columns': ['slug', 'name'], 'time_series_pattern': 'monthly', - 'time_series_columns': ['__balance_quan__', '__balance__'], + 'time_series_columns': ['__balance_quantity__', '__balance__'], } group_by = 'product' columns = ['slug', 'name'] time_series_pattern = 'monthly', - time_series_columns = ['__balance_quan__', '__balance__'] + time_series_columns = ['__balance_quantity__', '__balance__'] chart_settings = [ { diff --git a/tests/test_generator.py b/tests/test_generator.py index 6d53537..ae0d8bd 100644 --- a/tests/test_generator.py +++ b/tests/test_generator.py @@ -30,7 +30,7 @@ def test_get_crosstab_columns(self): self.assertEqual(len(columns), 4) report = CrosstabOnClient(crosstab_ids=[self.client1.pk, self.client2.pk], - crosstab_columns=['__total_quantity__', '__balance_quan__']) + crosstab_columns=['__total_quantity__', '__balance_quantity__']) columns = report.get_list_display_columns() self.assertEqual(len(columns), 8, [x['name'] for x in columns]) From b542eacf7172f5ac5c5ed6abe87cc0b4a9275735 Mon Sep 17 00:00:00 2001 From: Ramez Issac <ramez@rasystems.io> Date: Fri, 24 Jul 2020 13:10:35 +0200 Subject: [PATCH 6/6] version 0.2.7 --- CHANGELOG.md | 6 ++++++ docs/source/conf.py | 2 +- slick_reporting/__init__.py | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89a4489..3081238 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog All notable changes to this project will be documented in this file. +## [0.2.7] - 2020-07-24 +### Updates +- Bring back crosstab capability +- Rename `quan` to the more verbose `quantity` +- Minor enhancements around templates + ## [0.2.6] - 2020-06-06 ### Added diff --git a/docs/source/conf.py b/docs/source/conf.py index dab530c..14ab01c 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.1.3' +release = '0.2.7' # -- General configuration --------------------------------------------------- diff --git a/slick_reporting/__init__.py b/slick_reporting/__init__.py index 6988fd4..3e3df01 100644 --- a/slick_reporting/__init__.py +++ b/slick_reporting/__init__.py @@ -1,6 +1,6 @@ default_app_config = 'slick_reporting.apps.ReportAppConfig' -VERSION = (0, 2, 6) +VERSION = (0, 2, 7) -__version__ = '0.2.6' +__version__ = '0.2.7'