Skip to content

Commit

Permalink
Merge branch 'release/v0.5.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
RamezIssac committed Dec 17, 2020
2 parents 5d20886 + b0121f8 commit 5427538
Show file tree
Hide file tree
Showing 9 changed files with 55 additions and 61 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

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

## [0.5.1] -
## [0.5.2]

- Enhanced Time Series Plot total HighChart by accenting the categories
- Enhanced the default verbose names of time series.
- Expanding test coverage


## [0.5.1]

- Allow for time series to operate on a non-group by report
- Allow setting time series custom dates on ReportGenerator attr and init
Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
master_doc = 'index'

# The full version, including alpha/beta/rc tags
release = '0.5.1'
release = '0.5.2'

# -- 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,6 +1,6 @@

default_app_config = 'slick_reporting.apps.ReportAppConfig'

VERSION = (0, 5, 1)
VERSION = (0, 5, 2)

__version__ = '0.5.1'
__version__ = '0.5.2'
23 changes: 18 additions & 5 deletions slick_reporting/fields.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import uuid

from django.db.models import Sum
from django.template.defaultfilters import date as date_filter
from django.utils.translation import ugettext_lazy as _

from .decorators import report_field_register
from .helpers import get_calculation_annotation
from .registry import field_registry

Expand Down Expand Up @@ -284,15 +284,28 @@ def get_crosstab_field_verbose_name(cls, model, id):
return f'{cls.verbose_name} {model} {id}'

@classmethod
def get_time_series_field_verbose_name(cls, date_period):
def get_time_series_field_verbose_name(cls, date_period, index, dates, pattern):
"""
Sent the column data to construct a verbose name.
Default implemenetation is column name + the end date %Y%m%d
:param column_name: the computation field_name
Get the name of the verbose name of a computaion field that's in a time_series.
should be a mix of the date period if the column an it's verbose name.
:param date_period: a tuple of (start_date, end_date)
:param index: the index of the current field in the whole dates to be calculated
:param dates a list of tuples representing the start and the end date
:return: a verbose string
"""
dt_format = '%Y/%m/%d'

if pattern == 'monthly':
month_name = date_filter(date_period[0], 'F Y')
return f'{cls.verbose_name} {month_name}'
elif pattern == 'daily':
return f'{cls.verbose_name} {date_period[0].strftime(dt_format)}'
elif pattern == 'weekly':
return f' {cls.verbose_name} {_("Week")} {index} {date_period[0].strftime(dt_format)}'
elif pattern == 'yearly':
year = date_filter(date_period[0], 'Y')
return f'{cls.verbose_name} {year}'

return f'{cls.verbose_name} {date_period[0].strftime(dt_format)} - {date_period[1].strftime(dt_format)}'


Expand Down
2 changes: 0 additions & 2 deletions slick_reporting/form_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ class BaseReportForm:

@property
def media(self):
# media = super().media
from .app_settings import SLICK_REPORTING_FORM_MEDIA
# import pdb; pdb.set_trace()
return forms.Media(css=SLICK_REPORTING_FORM_MEDIA.get('css', {}), js=SLICK_REPORTING_FORM_MEDIA.get('js', []))

def get_filters(self):
Expand Down
14 changes: 6 additions & 8 deletions slick_reporting/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,6 @@ def __init__(self, report_model=None, main_queryset=None, start_date=None, end_d
# group_by_filter = self.kwargs_filters.get(self.group_by, '')
# qs = self.group_by_field.related_model.objects
# if group_by_filter:
# import pdb; pdb.set_trace()
# lookup = 'pk__in' if isinstance(group_by_filter, Iterable) else 'pk'
# qs = qs.filter(**{lookup: group_by_filter})
# self.main_queryset = qs.values()
Expand Down Expand Up @@ -300,11 +299,8 @@ def _prepare_report_dependencies(self):
fields_on_report = [x for x in window_cols if x['ref'] in dependencies_names]
for field in fields_on_report:
self._report_fields_dependencies[window][field['name']] = col_data['name']
# import pdb; pdb.set_trace()
for col_data in window_cols:
klass = col_data['ref']
# if getattr(klass, 'name', '') not in klasses_names:
# continue
name = col_data['name']

# if column has a dependency then skip it
Expand Down Expand Up @@ -519,7 +515,7 @@ def get_time_series_parsed_columns(self):
cols = self.time_series_columns or []
series = self._get_time_series_dates(self.time_series_pattern)

for dt in series:
for index, dt in enumerate(series):
for col in cols:
magic_field_class = None

Expand All @@ -531,7 +527,7 @@ def get_time_series_parsed_columns(self):
_values.append({
'name': magic_field_class.name + 'TS' + dt[1].strftime('%Y%m%d'),
'original_name': magic_field_class.name,
'verbose_name': self.get_time_series_field_verbose_name(magic_field_class, dt),
'verbose_name': self.get_time_series_field_verbose_name(magic_field_class, dt, index, series),
'ref': magic_field_class,
'start_date': dt[0],
'end_date': dt[1],
Expand All @@ -540,7 +536,7 @@ def get_time_series_parsed_columns(self):
})
return _values

def get_time_series_field_verbose_name(self, computation_class, date_period):
def get_time_series_field_verbose_name(self, computation_class, date_period, index, series, pattern=None):
"""
Sent the column data to construct a verbose name.
Default implementation is delegated to the ReportField.get_time_series_field_verbose_name
Expand All @@ -550,7 +546,9 @@ def get_time_series_field_verbose_name(self, computation_class, date_period):
:param date_period: a tuple of (start_date, end_date)
:return: a verbose string
"""
return computation_class.get_time_series_field_verbose_name(date_period)
pattern = pattern or self.time_series_pattern
return computation_class.get_time_series_field_verbose_name(date_period, index, series,
pattern)

def get_custom_time_series_dates(self):
"""
Expand Down
13 changes: 7 additions & 6 deletions slick_reporting/static/slick_reporting/ra.highchart.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,16 +360,17 @@
Object.keys(data_sources).forEach(function (series_cols, index) {
let data = []
data_sources[series_cols].forEach(function (col, index) {
data.push(totalValues[col])
})
series.push({
'name': 'Total', //todo
'data': data

series.push({
'name': response.metadata.time_series_column_verbose_names[index],
data: [totalValues[col]]
})
})

})
}
return {
// 'categories': response.metadata.time_series_column_verbose_names,
'categories': response.metadata.time_series_column_verbose_names,
'titles': response.metadata.time_series_column_verbose_names,
'series': series,
}
Expand Down
10 changes: 9 additions & 1 deletion tests/test_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,30 @@ def test_time_series_columns_inclusion(self):
self.assertEqual(len(x.get_list_display_columns()), 13)

def test_time_series_patterns(self):
from slick_reporting.fields import TotalReportField
report = ReportGenerator(OrderLine, date_field='order__date_placed', group_by='client',
columns=['name', '__time_series__'],
time_series_columns=['__total_quantity__'], time_series_pattern='monthly',
start_date=datetime(2020, 1, 1, tzinfo=pytz.timezone('utc')),
end_date=datetime(2020, 12, 31, tzinfo=pytz.timezone('utc')))

report_field_class = TotalReportField
dates = report._get_time_series_dates()
self.assertEqual(len(dates), 12)
self.assertIsNotNone(report.get_time_series_field_verbose_name(TotalReportField, dates[0], 0, dates))

dates = report._get_time_series_dates('daily')
self.assertEqual(len(dates), 365, len(dates))
self.assertIsNotNone(report.get_time_series_field_verbose_name(TotalReportField, dates[0], 0, dates, 'daily'))

dates = report._get_time_series_dates('weekly')
self.assertEqual(len(dates), 53, len(dates))
self.assertIsNotNone(report.get_time_series_field_verbose_name(TotalReportField, dates[0], 0, dates, 'weekly'))

dates = report._get_time_series_dates('semimonthly')
self.assertEqual(len(dates), 27, len(dates))
self.assertIsNotNone(
report.get_time_series_field_verbose_name(TotalReportField, dates[0], 0, dates, 'semimonthly'))

dates = report._get_time_series_dates('quarterly')
self.assertEqual(len(dates), 4, len(dates))
Expand All @@ -78,7 +86,7 @@ def test_time_series_patterns(self):
self.assertEqual(len(dates), 2, len(dates))
dates = report._get_time_series_dates('annually')
self.assertEqual(len(dates), 1, len(dates))

self.assertIsNotNone(report.get_time_series_field_verbose_name(TotalReportField, dates[0], 0, dates))

def test_time_series_custom_pattern(self):
# report = ReportGenerator(OrderLine, date_field='order__date_placed', group_by='client',
Expand Down
39 changes: 4 additions & 35 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,33 +126,6 @@ def test_productclientsalesmatrix(self):
self.assertEqual(data[0]['__total__CT%s' % self.client2.pk], 600)
self.assertEqual(data[0]['__total__CT----'], 900)

def _test_default_order_by(self):
self.client.login(username='super', password='secret')
response = self.client.get(reverse('ra_admin:report', args=('client', 'clienttotalbalancesordered')),
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
data = response.json()
previous_balance = 0
self.assertTrue(len(data['data']) > 1)
for i, line in enumerate(data['data']):
if i == 0:
previous_balance = line['__balance__']
else:
self.assertTrue(line['__balance__'] > previous_balance)

def _test_default_order_by_reversed(self):
self.client.login(username='super', password='secret')
response = self.client.get(reverse('ra_admin:report', args=('client', 'ClientTotalBalancesOrderedDESC')),
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
data = response.json()
previous_balance = 0
self.assertTrue(len(data['data']) > 1)
for i, line in enumerate(data['data']):
if i == 0:
previous_balance = line['__balance__']
else:
self.assertTrue(line['__balance__'] < previous_balance)

def test_show_empty_records(self):
report = report_generators.ClientTotalBalance()
Expand Down Expand Up @@ -252,6 +225,8 @@ def test_crosstab_report_view(self):
data = ProductClientSalesMatrix(crosstab_compute_reminder=True,
crosstab_ids=[self.client1.pk, self.client2.pk]).get_report_data()

response = self.client.get(reverse('product_crosstab_client'))
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse('product_crosstab_client'), data={
'client_id': [self.client1.pk, self.client2.pk],
'crosstab_compute_reminder': True,
Expand Down Expand Up @@ -283,10 +258,6 @@ def test_chart_settings(self):
self.assertTrue('pie' in data['chart_settings'][0]['id'])
self.assertTrue(data['chart_settings'][0]['title'], 'awesome report title')

def _test_column_names_are_always_strings(self):
# todo
pass

def test_error_on_missing_date_field(self):
def test_function():
class TotalClientSales(SlickReportView):
Expand Down Expand Up @@ -333,12 +304,10 @@ def register():

def test_get_non_existent_field(self):
def register():
field = field_registry.get_field_by_name('__a_weird_name__')
return field
return field_registry.get_field_by_name('__a_weird_name__')

with self.assertRaises(Exception):
field = register()
self.assertIsNone(field)
register()

def test_creating_a_report_field_on_the_fly(self):
from django.db.models import Sum
Expand Down

0 comments on commit 5427538

Please sign in to comment.