Skip to content

Commit

Permalink
Merge branch 'release/0.6.6'
Browse files Browse the repository at this point in the history
  • Loading branch information
Ramez Ashraf committed Feb 10, 2023
2 parents a840d48 + 6dd03a6 commit cdf8d8b
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 37 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ language: python
python:
- "3.8"
- "3.9"
- "3.10"

env:
- DJANGO=django==3.2.15
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

All notable changes to this project will be documented in this file.

## [0.6.6]

- Now a method on a generator can be effectively used as column
- Use correct model when traversing on group by


## [0.6.5]
- Fix Issue with group_by field pointing to model with custom primary key Issue #58

Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
master_doc = 'index'

# The full version, including alpha/beta/rc tags
release = '0.6.5'
release = '0.6.6'

# -- General configuration ---------------------------------------------------

Expand Down
4 changes: 2 additions & 2 deletions slick_reporting/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
default_app_config = 'slick_reporting.apps.ReportAppConfig'

VERSION = (0, 6, 5)
VERSION = (0, 6, 6)

__version__ = '0.6.5'
__version__ = '0.6.6'
21 changes: 14 additions & 7 deletions slick_reporting/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,10 @@ 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.get_fields() if x.name == search_field][0]
self.group_by_field = get_field_from_query_text(self.group_by, self.report_model)

except IndexError:
except (IndexError, AttributeError):
raise ImproperlyConfigured(
f'Can not find group_by field:{self.group_by} in report_model {self.report_model} ')
if '__' not in self.group_by:
Expand Down Expand Up @@ -245,7 +243,7 @@ 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 and '__' not in self.group_by:
if type(self.group_by_field) is ForeignKey:
ids = self.main_queryset.values_list(self.group_by_field_attname).distinct()
# uses the same logic that is in Django's query.py when fields is empty in values() call
concrete_fields = [f.name for f in self.group_by_field.related_model._meta.concrete_fields]
Expand Down Expand Up @@ -369,7 +367,10 @@ def _get_record_data(self, obj, columns):

name = col_data['name']

if (col_data.get('source', '') == 'magic_field' and self.group_by) or (
if col_data.get('source', '') == 'attribute_field':
data[name] = col_data['ref'](self, obj, data)

elif (col_data.get('source', '') == 'magic_field' and self.group_by) or (
self.time_series_pattern and not self.group_by):
source = self._report_fields_dependencies[window].get(name, False)
if source:
Expand Down Expand Up @@ -420,6 +421,7 @@ def check_columns(cls, columns, group_by, report_model, ):
:param report_model: the report model
:return: List of dict, each dict contains relevant data to the respective field in `columns`
"""
group_by_field = ''
group_by_model = None
if group_by:
try:
Expand Down Expand Up @@ -457,7 +459,7 @@ def check_columns(cls, columns, group_by, report_model, ):
if attribute_field:
col_data = {'name': col,
'verbose_name': getattr(attribute_field, 'verbose_name', col),
# 'type': 'method',
'source': 'attribute_field',
'ref': attribute_field,
'type': 'text'
}
Expand All @@ -473,6 +475,11 @@ def check_columns(cls, columns, group_by, report_model, ):
else:
# A database field
model_to_use = group_by_model if group_by and '__' not in group_by else report_model
group_by_str = str(group_by)
if '__' in group_by_str:
related_model = get_field_from_query_text(group_by, model_to_use).related_model
model_to_use = related_model if related_model else model_to_use

try:
if '__' in col:
# A traversing link order__client__email
Expand Down
6 changes: 6 additions & 0 deletions tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,14 @@ class Meta:
verbose_name_plural = _('Products')


class Agent(models.Model):
name = models.CharField(max_length=200, verbose_name=_('Name'))


class Contact(models.Model):
address = models.CharField(max_length=200, verbose_name=_('Name'))
po_box = models.CharField(max_length=200, verbose_name=_('po_box'), null=True, blank=True)
agent = models.ForeignKey(Agent, on_delete=models.CASCADE)


class Client(models.Model):
Expand Down
17 changes: 11 additions & 6 deletions tests/report_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class GeneratorWithAttrAsColumn(GenericGenerator):
def get_data(self, obj):
return ''

get_data.verbose_name = 'get_data_verbose_name'
get_data.verbose_name = 'My Verbose Name'


class CrosstabOnClient(GenericGenerator):
Expand Down Expand Up @@ -79,7 +79,16 @@ class ProductTotalSales(ReportGenerator):
report_model = SimpleSales
date_field = 'doc_date'
group_by = 'product'
columns = ['slug', 'name', '__balance__', '__balance_quantity__']
columns = ['slug', 'name', '__balance__', '__balance_quantity__', 'get_object_sku', 'average_value']


def get_object_sku(self, obj, data):
return obj['sku'].upper()
get_object_sku.verbose_name = 'SKU ALL CAPS'

def average_value(self, obj, data):
return data['__balance__'] / data['__balance_quantity__']
average_value.verbose_name = 'Average Value'


class ProductTotalSalesProductWithCustomID(ReportGenerator):
Expand Down Expand Up @@ -248,10 +257,6 @@ class ProductClientSalesMatrix2(ReportGenerator):
crosstab_columns = [SlickReportField.create(Sum, 'value', name='value__sum', verbose_name=_('Sales'))]


class GeneratorClassWithAttrsAs(ReportGenerator):
columns = ['get_icon', 'slug', 'name']


class ClientTotalBalancesWithShowEmptyFalse(ClientTotalBalance):
report_slug = None
default_order_by = '-__balance__'
Expand Down
41 changes: 27 additions & 14 deletions tests/test_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ def not_known_pattern():

self.assertRaises(Exception, not_known_pattern)


def test_time_series_custom_pattern(self):
# report = ReportGenerator(OrderLine, date_field='order__date_placed', group_by='client',
# columns=['name', '__time_series__'],
Expand All @@ -140,11 +139,7 @@ def test_attr_as_column(self):
report = GeneratorWithAttrAsColumn()
columns_data = report.get_list_display_columns()
self.assertEqual(len(columns_data), 3)

self.assertEqual(columns_data[0]['verbose_name'], 'get_data_verbose_name')
data = report.get_report_data()
self.assertIsNot(data, [])
# todo
self.assertEqual(columns_data[0]['verbose_name'], 'My Verbose Name')

def test_improper_group_by(self):
def load():
Expand Down Expand Up @@ -177,11 +172,6 @@ def load():

self.assertRaises(Exception, load)

def _test_attr_called(self):
report = GeneratorWithAttrAsColumn()
data = report.get_report_data()
# self.assertEqual(len(report.get_list_display_columns()), 3)

def test_gather_dependencies_for_time_series(self):
report = ReportGenerator(report_model=SimpleSales, group_by='client',
columns=['slug', 'name'],
Expand All @@ -202,15 +192,14 @@ def test_group_by_traverse(self):

self.assertTrue(report._report_fields_dependencies)
data = report.get_report_data()
# import pdb;
# pdb.set_trace()
self.assertNotEqual(data, [])
self.assertEqual(data[0]['product__category'], 'small')
self.assertEqual(data[1]['product__category'], 'big')

def test_group_by_and_foreign_key_field(self):
report = ReportGenerator(report_model=SimpleSales, group_by='client',
columns=['name', 'contact_id', 'contact__address', SlickReportField.create(Sum, 'value'), '__total__'],
columns=['name', 'contact_id', 'contact__address',
SlickReportField.create(Sum, 'value'), '__total__'],
# time_series_pattern='monthly',
date_field='doc_date',
# time_series_columns=['__debit__', '__credit__', '__balance__', '__total__']
Expand All @@ -236,6 +225,30 @@ def test_group_by_and_foreign_key_field(self):
self.assertEqual(data[1]['contact__address'], 'Street 2')
self.assertEqual(data[2]['contact__address'], 'Street 3')

def test_traversing_group_by_and_foreign_key_field(self):
report = ReportGenerator(report_model=SimpleSales, group_by='client__contact',
columns=['po_box', 'address', 'agent__name',
SlickReportField.create(Sum, 'value'), '__total__'],
date_field='doc_date')

self.assertTrue(report._report_fields_dependencies)
data = report.get_report_data()
self.assertNotEqual(data, [])
# self.assertTrue(False)
self.assertEqual(data[0]['address'], 'Street 1')
self.assertEqual(data[1]['address'], 'Street 2')
self.assertEqual(data[1]['agent__name'], 'John')
self.assertEqual(data[2]['agent__name'], 'Frank')

def test_traversing_group_by_sanity(self):
report = ReportGenerator(report_model=SimpleSales, group_by='client__contact__agent',
columns=['name', SlickReportField.create(Sum, 'value'), '__total__'],
date_field='doc_date', )

self.assertTrue(report._report_fields_dependencies)
data = report.get_report_data()
self.assertNotEqual(data, [])
self.assertEqual(len(data), 2)

def test_db_field_column_verbose_name(self):
report = GenericGenerator()
Expand Down
18 changes: 11 additions & 7 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
GroupByCharFieldPlusTimeSeries, TimeSeriesWithOutGroupBy
from . import report_generators
from .models import Client, Contact, Product, SimpleSales, UserJoined, SalesWithFlag, ComplexSales, TaxCode, \
ProductCustomID, SalesProductWithCustomID
ProductCustomID, SalesProductWithCustomID, Agent
from .views import SlickReportView

User = get_user_model()
Expand All @@ -35,20 +35,22 @@ def setUpTestData(cls):
password='password')
cls.user = user
cls.limited_user = limited_user
agent = Agent.objects.create(name='John')
agent2 = Agent.objects.create(name='Frank')
cls.client1 = Client.objects.create(name='Client 1')
cls.client1.contact = Contact.objects.create(address='Street 1')
cls.client1.contact = Contact.objects.create(address='Street 1', agent=agent)
cls.client1.save()
cls.client2 = Client.objects.create(name='Client 2')
cls.client2.contact = Contact.objects.create(address='Street 2')
cls.client2.contact = Contact.objects.create(address='Street 2', agent=agent)
cls.client2.save()
cls.client3 = Client.objects.create(name='Client 3')
cls.client3.contact = Contact.objects.create(address='Street 3')
cls.client3.contact = Contact.objects.create(address='Street 3' , agent=agent2)
cls.client3.save()
cls.clientIdle = Client.objects.create(name='Client Idle')

cls.product1 = Product.objects.create(name='Product 1', category='small')
cls.product2 = Product.objects.create(name='Product 2', category='medium')
cls.product3 = Product.objects.create(name='Product 3', category='big')
cls.product1 = Product.objects.create(name='Product 1', category='small', sku='a1b1')
cls.product2 = Product.objects.create(name='Product 2', category='medium', sku='a2b2')
cls.product3 = Product.objects.create(name='Product 3', category='big', sku='3333')

cls.product_w_custom_id1 = ProductCustomID.objects.create(name='Product 1', category='small')
cls.product_w_custom_id2 = ProductCustomID.objects.create(name='Product 2', category='medium')
Expand Down Expand Up @@ -143,6 +145,8 @@ def test_product_total_sales_product_custom_id(self):
report = report_generators.ProductTotalSales()
data = report.get_report_data()
self.assertEqual(data[0]['__balance__'], 1800)
self.assertEqual(data[0]['get_object_sku'], 'A1B1')
self.assertEqual(data[0]['average_value'], data[0]['__balance__']/data[0]['__balance_quantity__'])

def test_product_total_sales_with_percentage(self):
report = report_generators.ProductTotalSalesWithPercentage()
Expand Down

0 comments on commit cdf8d8b

Please sign in to comment.