From 25c48666ddeba6a0869ae93cc6bce0effb6775ce Mon Sep 17 00:00:00 2001 From: Hannah Eslinger Date: Mon, 4 Dec 2023 15:57:53 -0700 Subject: [PATCH] Populate default report using cycles (#4420) * Populate default report using cycles * Remove console.logs * Fix api * update css a bit --------- Co-authored-by: kflemin <2205659+kflemin@users.noreply.github.com> --- .../export_report_modal_controller.js | 7 +- .../inventory_reports_controller.js | 79 +++---- .../js/services/inventory_reports_service.js | 21 +- .../seed/partials/inventory_reports.html | 56 ++--- seed/static/seed/scss/style.scss | 10 + seed/tests/test_reports.py | 76 +++++-- seed/views/v3/organizations.py | 211 +++++++----------- 7 files changed, 218 insertions(+), 242 deletions(-) diff --git a/seed/static/seed/js/controllers/export_report_modal_controller.js b/seed/static/seed/js/controllers/export_report_modal_controller.js index 630a351621..dda327f45e 100644 --- a/seed/static/seed/js/controllers/export_report_modal_controller.js +++ b/seed/static/seed/js/controllers/export_report_modal_controller.js @@ -6,11 +6,10 @@ angular.module('BE.seed.controller.export_report_modal', []).controller('export_ '$scope', '$uibModalInstance', 'axes_data', - 'cycle_start', - 'cycle_end', + 'cycles', 'inventory_reports_service', // eslint-disable-next-line func-names - function ($scope, $uibModalInstance, axes_data, cycle_start, cycle_end, inventory_reports_service) { + function ($scope, $uibModalInstance, axes_data, cycles, inventory_reports_service) { $scope.export_name = ''; $scope.export_selected = () => { @@ -21,7 +20,7 @@ angular.module('BE.seed.controller.export_report_modal', []).controller('export_ const ext = '.xlsx'; if (!filename.endsWith(ext)) filename += ext; - inventory_reports_service.export_reports_data(axes_data, cycle_start, cycle_end).then((response) => { + inventory_reports_service.export_reports_data(axes_data, cycles).then((response) => { const blob_type = response.headers()['content-type']; const blob = new Blob([response.data], { type: blob_type }); diff --git a/seed/static/seed/js/controllers/inventory_reports_controller.js b/seed/static/seed/js/controllers/inventory_reports_controller.js index ebb6f1af07..744460a36c 100644 --- a/seed/static/seed/js/controllers/inventory_reports_controller.js +++ b/seed/static/seed/js/controllers/inventory_reports_controller.js @@ -54,10 +54,6 @@ angular.module('BE.seed.controller.inventory_reports', []).controller('inventory /* Setup models from "From" and "To" selectors */ $scope.cycles = cycles.cycles; - /* Model for pulldowns, initialized in init below */ - $scope.fromCycle = {}; - $scope.toCycle = {}; - const translateAxisLabel = (label, units) => { let str = ''; str += $translate.instant(label); @@ -123,15 +119,6 @@ angular.module('BE.seed.controller.inventory_reports', []).controller('inventory $scope.chart1Title = ''; $scope.chart2Title = ''; - // Datepickers - const initStartDate = new Date(); - initStartDate.setYear(initStartDate.getFullYear() - 1); - $scope.startDate = initStartDate; - $scope.startDatePickerOpen = false; - $scope.endDate = new Date(); - $scope.endDatePickerOpen = false; - $scope.invalidDates = false; // set this to true when startDate >= endDate; - // Series // the following variable keeps track of which // series will be sent to the graphs when data is updated @@ -252,34 +239,33 @@ angular.module('BE.seed.controller.inventory_reports', []).controller('inventory // specific styling for scatter chart $scope.scatterChart.options.scales.x.suggestedMin = 0; - /* END NEW CHART STUFF */ - - /* UI HANDLERS */ - /* ~~~~~~~~~~~ */ - - // Handle datepicker open/close events - $scope.openStartDatePicker = ($event) => { - $event.preventDefault(); - $event.stopPropagation(); - $scope.startDatePickerOpen = !$scope.startDatePickerOpen; + $scope.cycle_selection = ''; + $scope.selected_cycles = []; + $scope.available_cycles = () => $scope.cycles.filter(({ id }) => !$scope.selected_cycles.includes(id)); + $scope.select_cycle = () => { + const selection = $scope.cycle_selection; + $scope.cycle_selection = ''; + if (!$scope.selected_cycles) { + $scope.selected_cycles = []; + } + $scope.selected_cycles.push(selection); }; - $scope.openEndDatePicker = ($event) => { - $event.preventDefault(); - $event.stopPropagation(); - $scope.endDatePickerOpen = !$scope.endDatePickerOpen; + + $scope.get_cycle_display = (id) => { + const record = _.find($scope.cycles, { id }); + if (record) { + return record.name; + } }; - $scope.$watch('startDate', () => { - $scope.checkInvalidDate(); - }); + $scope.click_remove_cycle = (id) => { + $scope.selected_cycles = $scope.selected_cycles.filter((item) => item !== id); + }; - $scope.$watch('endDate', () => { - $scope.checkInvalidDate(); - }); + /* END NEW CHART STUFF */ - $scope.checkInvalidDate = () => { - $scope.invalidDates = $scope.endDate < $scope.startDate; - }; + /* UI HANDLERS */ + /* ~~~~~~~~~~~ */ /* Update data used by the chart. This will force the charts to re-render */ $scope.updateChartData = () => { @@ -374,8 +360,7 @@ angular.module('BE.seed.controller.inventory_reports', []).controller('inventory yVar: $scope.chartData.yAxisVarName, yLabel: $scope.chartData.yAxisTitle }), - cycle_start: () => $scope.fromCycle.selected_cycle.start, - cycle_end: () => $scope.toCycle.selected_cycle.end + cycles: () => $scope.selected_cycles, } }); }; @@ -398,7 +383,7 @@ angular.module('BE.seed.controller.inventory_reports', []).controller('inventory $scope.chartIsLoading = true; inventory_reports_service - .get_report_data(xVar, yVar, $scope.fromCycle.selected_cycle.start, $scope.toCycle.selected_cycle.end) + .get_report_data(xVar, yVar, $scope.selected_cycles) .then( (data) => { data = data.data; @@ -462,7 +447,7 @@ angular.module('BE.seed.controller.inventory_reports', []).controller('inventory const yVar = $scope.yAxisSelectedItem.varName; $scope.aggChartIsLoading = true; inventory_reports_service - .get_aggregated_report_data(xVar, yVar, $scope.fromCycle.selected_cycle.start, $scope.toCycle.selected_cycle.end) + .get_aggregated_report_data(xVar, yVar, $scope.selected_cycles) .then( (data) => { data = data.aggregated_data; @@ -506,9 +491,7 @@ angular.module('BE.seed.controller.inventory_reports', []).controller('inventory // Save axis and cycle selections localStorage.setItem(localStorageXAxisKey, JSON.stringify($scope.xAxisSelectedItem)); localStorage.setItem(localStorageYAxisKey, JSON.stringify($scope.yAxisSelectedItem)); - - localStorage.setItem(localStorageFromCycleKey, JSON.stringify($scope.fromCycle.selected_cycle)); - localStorage.setItem(localStorageToCycleKey, JSON.stringify($scope.toCycle.selected_cycle)); + localStorage.setItem(localStorageSelectedCycles, JSON.stringify($scope.selected_cycles)); } /* Generate an array of color objects to be used as part of chart configuration @@ -536,19 +519,13 @@ angular.module('BE.seed.controller.inventory_reports', []).controller('inventory return colorsArr; } - var localStorageFromCycleKey = `${base_storage_key}.fromcycle`; - var localStorageToCycleKey = `${base_storage_key}.tocycle`; + var localStorageSelectedCycles = `${base_storage_key}.SelectedCycles`; /* Call the update method so the page initializes with the values set in the scope */ function init() { // Initialize pulldowns - $scope.fromCycle = { - selected_cycle: JSON.parse(localStorage.getItem(localStorageFromCycleKey)) || _.head($scope.cycles) - }; - $scope.toCycle = { - selected_cycle: JSON.parse(localStorage.getItem(localStorageToCycleKey)) || _.last($scope.cycles) - }; + $scope.selected_cycles = JSON.parse(localStorage.getItem(localStorageSelectedCycles)) || [] // Attempt to load selections $scope.updateChartData(); diff --git a/seed/static/seed/js/services/inventory_reports_service.js b/seed/static/seed/js/services/inventory_reports_service.js index 242b597d7e..9f6d436f72 100644 --- a/seed/static/seed/js/services/inventory_reports_service.js +++ b/seed/static/seed/js/services/inventory_reports_service.js @@ -34,9 +34,9 @@ angular.module('BE.seed.service.inventory_reports', []).factory('inventory_repor ] } */ - const get_report_data = (xVar, yVar, start, end) => { + const get_report_data = (xVar, yVar, cycle_ids) => { // Error checks - if (_.some([xVar, yVar, start, end], _.isNil)) { + if (_.some([xVar, yVar, cycle_ids], _.isNil)) { $log.error('#inventory_reports_service.get_report_data(): null parameter'); throw new Error('Invalid Parameter'); } @@ -47,8 +47,7 @@ angular.module('BE.seed.service.inventory_reports', []).factory('inventory_repor params: { x_var: xVar, y_var: yVar, - start, - end + cycle_ids, } }) .then((response) => response.data) @@ -83,9 +82,9 @@ angular.module('BE.seed.service.inventory_reports', []).factory('inventory_repor } } */ - const get_aggregated_report_data = (xVar, yVar, start, end) => { + const get_aggregated_report_data = (xVar, yVar, cycle_ids) => { // Error checks - if (_.some([xVar, yVar, start, end], _.isNil)) { + if (_.some([xVar, yVar, cycle_ids], _.isNil)) { $log.error('#inventory_reports_service.get_aggregated_report_data(): null parameter'); throw new Error('Invalid Parameter'); } @@ -96,21 +95,20 @@ angular.module('BE.seed.service.inventory_reports', []).factory('inventory_repor params: { x_var: xVar, y_var: yVar, - start, - end + cycle_ids, } }) .then((response) => response.data) .catch(() => {}); }; - const export_reports_data = (axes_data, start, end) => { + const export_reports_data = (axes_data, cycle_ids) => { const { xVar } = axes_data; const { xLabel } = axes_data; const { yVar } = axes_data; const { yLabel } = axes_data; // Error checks - if (_.some([xVar, xLabel, yVar, yLabel, start, end], _.isNil)) { + if (_.some([xVar, xLabel, yVar, yLabel, cycle_ids], _.isNil)) { $log.error('#inventory_reports_service.get_aggregated_report_data(): null parameter'); throw new Error('Invalid Parameter'); } @@ -123,8 +121,7 @@ angular.module('BE.seed.service.inventory_reports', []).factory('inventory_repor x_label: xLabel, y_var: yVar, y_label: yLabel, - start, - end + cycle_ids, }, responseType: 'arraybuffer' }) diff --git a/seed/static/seed/partials/inventory_reports.html b/seed/static/seed/partials/inventory_reports.html index 30d09564ce..e140b5c2fa 100644 --- a/seed/static/seed/partials/inventory_reports.html +++ b/seed/static/seed/partials/inventory_reports.html @@ -19,42 +19,34 @@

Default Reports

{$:: 'Property Reports' | translate $}

-
-
-
- - -
- -
- - +
+ +
+
+ +
  • + {$:: get_cycle_display(item) $} + +
  • +
  • + +
  • +
    -
    - - -
    +
    +
    + + +
    -
    - - +
    + + +
    -
    diff --git a/seed/static/seed/scss/style.scss b/seed/static/seed/scss/style.scss index 473431333b..3a10d3ee77 100755 --- a/seed/static/seed/scss/style.scss +++ b/seed/static/seed/scss/style.scss @@ -3891,6 +3891,16 @@ $pairedCellWidth: 60px; padding-left: 5px; } +.pad-bottom-10 { + padding-bottom: 10px; +} + +.right-bar { + padding-left: 10px; + padding-right: 25px; + border-right: 1px solid #999; +} + .matching-column-header .ui-grid-header-cell-label { font-weight: bolder; } diff --git a/seed/tests/test_reports.py b/seed/tests/test_reports.py index 9eb8c1bef3..d9f202bf97 100644 --- a/seed/tests/test_reports.py +++ b/seed/tests/test_reports.py @@ -33,12 +33,9 @@ def setUp(self): 'email': 'test_user@demo.com' } self.client.login(**user_details) - - def test_report_export_excel_workbook(self): - cycle_factory = FakeCycleFactory(organization=self.org, user=self.user) + self.cycle_factory = FakeCycleFactory(organization=self.org, user=self.user) start = datetime.datetime(2016, 1, 1, tzinfo=timezone.get_current_timezone()) - # import pdb; pdb.set_trace() - self.cycle_2 = cycle_factory.get_cycle(name="Cycle 2", start=start) + self.cycle_2 = self.cycle_factory.get_cycle(name="Cycle 2", start=start) # create 5 records with site_eui and gross_floor_area in each cycle for i in range(1, 6): @@ -66,17 +63,17 @@ def test_report_export_excel_workbook(self): cycle_id=self.cycle_2.id ) + def test_report_export_excel_workbook(self): url = reverse('api:v3:organizations-report-export', args=[self.org.pk]) # needs to be turned into post? - response = self.client.get(url + '?{}={}&{}={}&{}={}&{}={}&{}={}&{}={}'.format( - 'start', '2014-12-31T00:00:00-07:53', - 'end', '2017-12-31T00:00:00-07:53', - 'x_var', 'site_eui', - 'x_label', 'Site EUI', - 'y_var', 'gross_floor_area', - 'y_label', 'Gross Floor Area' - )) + response = self.client.get(url, data={ + 'cycle_ids': [self.cycle.id, self.cycle_2.id], + 'x_var': 'site_eui', + 'x_label': 'Site EUI', + 'y_var': 'gross_floor_area', + 'y_label': 'Gross Floor Area', + }) self.assertEqual(200, response.status_code) @@ -110,3 +107,56 @@ def test_report_export_excel_workbook(self): self.assertEqual('Gross Floor Area', agg_sheet.cell(0, 1).value) self.assertEqual('0-99k', agg_sheet.cell(1, 1).value) self.assertEqual('0-99k', agg_sheet.cell(2, 1).value) + + def test_report(self): + url = reverse('api:v3:organizations-report', args=[self.org.pk]) + data = { + 'cycle_ids': [self.cycle.id, self.cycle_2.id], + 'x_var': 'site_eui', + 'y_var': 'gross_floor_area', + } + response = self.client.get(url, data) + + assert response.json()["data"]["property_counts"] == [ + {'yr_e': '2016', 'num_properties': 5, 'num_properties_w-data': 5}, + {'yr_e': '2015', 'num_properties': 5, 'num_properties_w-data': 5} + ] + + data["cycle_ids"] = [self.cycle.id] + response = self.client.get(url, data) + + assert response.json()["data"]["property_counts"] == [ + {'yr_e': '2015', 'num_properties': 5, 'num_properties_w-data': 5} + ] + + def test_report_aggregated(self): + url = reverse('api:v3:organizations-report-aggregated', args=[self.org.pk]) + data = { + 'cycle_ids': [self.cycle.id, self.cycle_2.id], + 'x_var': 'site_eui', + 'y_var': 'gross_floor_area', + } + response = self.client.get(url, data) + + assert response.json()["aggregated_data"]["property_counts"] == [ + {'yr_e': '2016', 'num_properties': 5, 'num_properties_w-data': 5}, + {'yr_e': '2015', 'num_properties': 5, 'num_properties_w-data': 5} + ] + + data["cycle_ids"] = [self.cycle.id] + response = self.client.get(url, data) + + assert response.json()["aggregated_data"]["property_counts"] == [ + {'yr_e': '2015', 'num_properties': 5, 'num_properties_w-data': 5} + ] + + def test_report_missing_arg(self): + url = reverse('api:v3:organizations-report-aggregated', args=[self.org.pk]) + data = { + 'cycle_ids': [self.cycle.id, self.cycle_2.id], + 'x_var': 'site_eui', + # 'y_var': 'gross_floor_area', it's missing! + } + response = self.client.get(url, data) + + assert response.status_code == 400 diff --git a/seed/views/v3/organizations.py b/seed/views/v3/organizations.py index 493ecedae9..ec8422858d 100644 --- a/seed/views/v3/organizations.py +++ b/seed/views/v3/organizations.py @@ -9,7 +9,6 @@ from io import BytesIO from pathlib import Path -import dateutil from django.conf import settings from django.contrib.auth.decorators import permission_required from django.contrib.auth.forms import PasswordResetForm @@ -20,7 +19,6 @@ from django.utils.decorators import method_decorator from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema -from past.builtins import basestring from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.response import Response @@ -785,35 +783,6 @@ def geocoding_columns(self, request, pk=None): return JsonResponse(geocoding_columns) - def get_cycles(self, start, end, organization_id): - if not isinstance(start, type(end)): - raise TypeError('start and end not same types') - # if of type int or convertable assume they are cycle ids - try: - start = int(start) - end = int(end) - except ValueError: - # assume string is JS date - if isinstance(start, basestring): - start_datetime = dateutil.parser.parse(start) - end_datetime = dateutil.parser.parse(end) - else: - raise Exception('Date is not a string') - # get date times from cycles - if isinstance(start, int): - cycle = Cycle.objects.get(pk=start, organization_id=organization_id) - start_datetime = cycle.start - if start == end: - end_datetime = cycle.end - else: - end_datetime = Cycle.objects.get( - pk=end, organization_id=organization_id - ).end - return Cycle.objects.filter( - start__gte=start_datetime, end__lte=end_datetime, - organization_id=organization_id - ).order_by('start') - def get_data(self, property_view, x_var, y_var, matching_columns): result = {} state = property_view.state @@ -896,42 +865,31 @@ def get_raw_report_data(self, organization_id, cycles, x_var, y_var, addtional_c def report(self, request, pk=None): """Retrieve a summary report for charting x vs y """ - params = {} - missing_params = [] - error = '' - for param in ['x_var', 'y_var', 'start', 'end']: - val = request.query_params.get(param, None) - if not val: - missing_params.append(param) - else: - params[param] = val + params = { + "x_var": request.query_params.get("x_var", None), + "y_var": request.query_params.get("y_var", None), + "cycle_ids": request.query_params.getlist("cycle_ids", None), + } + + excepted_params = ["x_var", "y_var", "cycle_ids"] + missing_params = [p for p in excepted_params if p not in params] if missing_params: - error = "{} Missing params: {}".format( - error, ", ".join(missing_params) + return Response( + {'status': 'error', 'message': "Missing params: {}".format(", ".join(missing_params))}, + status=status.HTTP_400_BAD_REQUEST ) - if error: - status_code = status.HTTP_400_BAD_REQUEST - result = {'status': 'error', 'message': error} - else: - cycles = self.get_cycles(params['start'], params['end'], pk) - data = self.get_raw_report_data( - pk, cycles, params['x_var'], params['y_var'] - ) - for datum in data: - if datum['property_counts']['num_properties_w-data'] != 0: - break - property_counts = [] - chart_data = [] - for datum in data: - property_counts.append(datum['property_counts']) - chart_data.extend(datum['chart_data']) - data = { - 'property_counts': property_counts, - 'chart_data': chart_data, - } - result = {'status': 'success', 'data': data} - status_code = status.HTTP_200_OK - return Response(result, status=status_code) + + cycles = Cycle.objects.filter(id__in=params["cycle_ids"]) + data = self.get_raw_report_data(pk, cycles, params['x_var'], params['y_var']) + data = { + "chart_data": sum([d["chart_data"] for d in data], []), + "property_counts": [d["property_counts"] for d in data] + } + + return Response( + {'status': 'success', 'data': data}, + status=status.HTTP_200_OK + ) @swagger_auto_schema( manual_parameters=[ @@ -964,59 +922,52 @@ def report(self, request, pk=None): def report_aggregated(self, request, pk=None): """Retrieve a summary report for charting x vs y aggregated by y_var """ - valid_y_values = ['gross_floor_area', 'property_type', 'year_built'] - params = {} - missing_params = [] - empty = True - error = '' - for param in ['x_var', 'y_var', 'start', 'end']: - val = request.query_params.get(param, None) - if not val: - missing_params.append(param) - elif param == 'y_var' and val not in valid_y_values: - error = "{} {} is not a valid value for {}.".format( - error, val, param - ) - else: - params[param] = val + # get params + params = { + "x_var": request.query_params.get("x_var", None), + "y_var": request.query_params.get("y_var", None), + "cycle_ids": request.query_params.getlist("cycle_ids", None) + } + + # error if missing + excepted_params = ["x_var", "y_var", "cycle_ids"] + missing_params = [p for p in excepted_params if p not in params] if missing_params: - error = "{} Missing params: {}".format( - error, ", ".join(missing_params) + return Response( + {'status': 'error', 'message': "Missing params: {}".format(", ".join(missing_params))}, + status=status.HTTP_400_BAD_REQUEST ) - if error: - status_code = status.HTTP_400_BAD_REQUEST - result = {'status': 'error', 'message': error} - else: - cycles = self.get_cycles(params['start'], params['end'], pk) - x_var = params['x_var'] - y_var = params['y_var'] - data = self.get_raw_report_data(pk, cycles, x_var, y_var) - for datum in data: - if datum['property_counts']['num_properties_w-data'] != 0: - empty = False - break - if empty: - result = {'status': 'error', 'message': 'No data found'} - status_code = status.HTTP_404_NOT_FOUND - if not empty or not error: - chart_data = [] - property_counts = [] - for datum in data: - buildings = datum['chart_data'] - yr_e = datum['property_counts']['yr_e'] - chart_data.extend(self.aggregate_data(yr_e, y_var, buildings)), - property_counts.append(datum['property_counts']) - # Send back to client - aggregated_data = { + + # error if y_var invalid + valid_y_values = ['gross_floor_area', 'property_type', 'year_built'] + if params["y_var"] not in valid_y_values: + return Response( + {'status': 'error', 'message': f"{params['y_var']} is not a valid value for y_var"}, + status=status.HTTP_400_BAD_REQUEST + ) + + # get data + cycles = Cycle.objects.filter(id__in=params["cycle_ids"]) + data = self.get_raw_report_data(pk, cycles, params["x_var"], params["y_var"]) + + chart_data = [] + property_counts = [] + for datum in data: + buildings = datum['chart_data'] + yr_e = datum['property_counts']['yr_e'] + chart_data.extend(self.aggregate_data(yr_e, params["y_var"], buildings)), + property_counts.append(datum['property_counts']) + + # Send back to client + result = { + 'status': 'success', + 'aggregated_data': { 'chart_data': chart_data, 'property_counts': property_counts - } - result = { - 'status': 'success', - 'aggregated_data': aggregated_data, - } - status_code = status.HTTP_200_OK - return Response(result, status=status_code) + }, + } + + return Response(result, status=status.HTTP_200_OK) def aggregate_data(self, yr_e, y_var, buildings): aggregation_method = { @@ -1141,23 +1092,23 @@ def report_export(self, request, pk=None): """ Export a report as a spreadsheet """ - params = {} - missing_params = [] - error = '' - for param in ['x_var', 'x_label', 'y_var', 'y_label', 'start', 'end']: - val = request.query_params.get(param, None) - if not val: - missing_params.append(param) - else: - params[param] = val + # get params + params = { + "x_var": request.query_params.get("x_var", None), + "x_label": request.query_params.get("x_label", None), + "y_var": request.query_params.get("y_var", None), + "y_label": request.query_params.get("y_label", None), + "cycle_ids": request.query_params.getlist("cycle_ids", None) + } + + # error if missing + excepted_params = ["x_var", "x_label", "y_var", "y_label", "cycle_ids"] + missing_params = [p for p in excepted_params if p not in params] if missing_params: - error = "{} Missing params: {}".format( - error, ", ".join(missing_params) + return Response( + {'status': 'error', 'message': "Missing params: {}".format(", ".join(missing_params))}, + status=status.HTTP_400_BAD_REQUEST ) - if error: - status_code = status.HTTP_400_BAD_REQUEST - result = {'status': 'error', 'message': error} - return Response(result, status=status_code) response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') response['Content-Disposition'] = 'attachment; filename="report-data"' @@ -1186,7 +1137,7 @@ def report_export(self, request, pk=None): agg_sheet.write(data_row_start, data_col_start + 2, 'Year Ending', bold) # Gather base data - cycles = self.get_cycles(params['start'], params['end'], pk) + cycles = Cycle.objects.filter(id__in=params["cycle_ids"]) matching_columns = Column.objects.filter(organization_id=pk, is_matching_criteria=True, table_name="PropertyState") data = self.get_raw_report_data( pk, cycles, params['x_var'], params['y_var'], matching_columns