Skip to content

Commit

Permalink
Speed-up-program-and-property-insight-pages
Browse files Browse the repository at this point in the history
  • Loading branch information
haneslinger committed Dec 4, 2024
1 parent dc590dc commit 8adda7f
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 50 deletions.
35 changes: 16 additions & 19 deletions seed/models/compliance_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,29 +66,26 @@ def evaluate(self, user_ali):
# get properties (no filter)
# property_response = properties_across_cycles(self.organization_id, -1, cycle_ids)
# get properties (applies filter group)
display_field_id = Column.objects.get(
display_field = Column.objects.get(
table_name="PropertyState", column_name=self.organization.property_display_field, organization=self.organization
).id
)
# array of columns to return
column_ids = [display_field_id]
columns = [display_field]

if self.actual_energy_column is not None:
column_ids.append(self.actual_energy_column.id)
columns.append(self.actual_energy_column)
if self.target_energy_column is not None:
column_ids.append(self.target_energy_column.id)
columns.append(self.target_energy_column)

if self.actual_emission_column is not None:
column_ids.append(self.actual_emission_column.id)
columns.append(self.actual_emission_column)
if self.target_emission_column is not None:
column_ids.append(self.target_emission_column.id)
columns.append(self.target_emission_column)

for col in self.x_axis_columns.all():
column_ids.append(col.id)
columns.append(col)

# Unique ids
column_ids = [*set(column_ids)]

property_response = properties_across_cycles_with_filters(self.organization_id, user_ali, cycle_ids, query_dict, column_ids)
property_response = properties_across_cycles_with_filters(self.organization_id, user_ali, cycle_ids, query_dict, columns)

datasets = {
"y": {"data": [], "label": "compliant"},
Expand Down Expand Up @@ -141,24 +138,24 @@ def evaluate(self, user_ali):

for p in property_response[cyc]:
# initialize
properties[p["property_view_id"]] = None
properties[p["id"]] = None
# energy metric
if metric["energy_metric"]:
properties[p["property_view_id"]] = self._calculate_compliance(p, metric["energy_bool"], "energy")
properties[p["id"]] = self._calculate_compliance(p, metric["energy_bool"], "energy")
# emission metric
if metric["emission_metric"] and properties[p["property_view_id"]] != "u":
if metric["emission_metric"] and properties[p["id"]] != "u":
temp_val = self._calculate_compliance(p, metric["emission_bool"], "emission")

# reconcile
if temp_val == "u":
# unknown stays unknown (missing data)
properties[p["property_view_id"]] = "u"
elif properties[p["property_view_id"]] is None:
properties[p["id"]] = "u"
elif properties[p["id"]] is None:
# only emission metric (not energy metric)
properties[p["property_view_id"]] = temp_val
properties[p["id"]] = temp_val
else:
# compliant if both are compliant
properties[p["property_view_id"]] = temp_val if temp_val == "n" else properties[p["property_view_id"]]
properties[p["id"]] = temp_val if temp_val == "n" else properties[p["id"]]

# count compliant, non-compliant, unknown for each property with data
for key in cnts:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ angular.module('SEED.controller.insights_property', []).controller('insights_pro
$scope.annotations = {};

_.forEach($scope.data.properties_by_cycles[$scope.configs.chart_cycle], (prop) => {
const item = { id: prop.property_view_id };
const item = { id: prop.id };
item.name = _.find(prop, (v, k) => k.startsWith($scope.organization.property_display_field));
// x axis is easy
item.x = _.find(prop, (v, k) => k.endsWith(`_${String($scope.configs.chart_xaxis)}`));
Expand All @@ -333,10 +333,10 @@ angular.module('SEED.controller.insights_property', []).controller('insights_pro
}

// place in appropriate dataset
if (_.includes($scope.data.results_by_cycles[$scope.configs.chart_cycle].y, prop.property_view_id)) {
if (_.includes($scope.data.results_by_cycles[$scope.configs.chart_cycle].y, prop.id)) {
// compliant dataset
datasets[0].data.push(item);
} else if (_.includes($scope.data.results_by_cycles[$scope.configs.chart_cycle].n, prop.property_view_id)) {
} else if (_.includes($scope.data.results_by_cycles[$scope.configs.chart_cycle].n, prop.id)) {
// non-compliant dataset
datasets[1].data.push(item);
} else {
Expand Down
66 changes: 38 additions & 28 deletions seed/utils/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import json
import logging

from django.db.models import F

# Imports from Django
from django.http import JsonResponse
from rest_framework import status
Expand All @@ -24,7 +26,6 @@
TaxLotView,
)
from seed.serializers.pint import apply_display_unit_preferences
from seed.utils.search import build_view_filters_and_sorts

logging.basicConfig(format="%(asctime)s %(levelname)-8s %(message)s", level=logging.ERROR, datefmt="%Y-%m-%d %H:%M:%S")

Expand Down Expand Up @@ -170,41 +171,50 @@ def properties_across_cycles(org_id, ali, profile_id, cycle_ids=[]):
return results


def properties_across_cycles_with_filters(org_id, user_ali, cycle_ids=[], query_dict={}, column_ids=[]):
# Identify column preferences to be used to scope fields/values
columns_from_database = Column.retrieve_all(org_id, "property", False)
org = Organization.objects.get(pk=org_id)
def properties_across_cycles_with_filters(org_id, user_ali, cycle_ids=[], query_dict={}, columns=[]):
# get relevant views
views_list = PropertyView.objects.select_related("property", "state", "cycle").filter(
property__organization_id=org_id,
cycle_id__in=cycle_ids,
property__access_level_instance__lft__gte=user_ali.lft,
property__access_level_instance__rgt__lte=user_ali.rgt,
)
views_list = _serialize_views(views_list, columns, org_id)

# group by cycle
results = {cycle_id: [] for cycle_id in cycle_ids}
property_views = _get_filter_group_views(org_id, cycle_ids, query_dict, user_ali)
views_cycle_ids = [v.cycle_id for v in property_views]
related_results = TaxLotProperty.serialize(property_views, column_ids, columns_from_database, include_related=False)
unit_collapsed_results = [apply_display_unit_preferences(org, x) for x in related_results]

for cycle_id, unit_collapsed_result in zip(views_cycle_ids, unit_collapsed_results):
results[cycle_id].append(unit_collapsed_result)
for view in views_list:
cycle_id = view["cycle_id"]
del view["cycle_id"]
results[cycle_id].append(view)

return results


# helper function for getting filtered properties
def _get_filter_group_views(org_id, cycles, query_dict, user_ali):
columns = Column.retrieve_all(org_id=org_id, inventory_type="property", only_used=False, include_related=False)
def _serialize_views(views_list, columns, org_id):
org = Organization.objects.get(pk=org_id)

# build annotations
annotations = {}
try:
filters, annotations, _order_by = build_view_filters_and_sorts(query_dict, columns, "property")
except Exception:
return JsonResponse({"status": "error", "message": "error with filter group"}, status=status.HTTP_404_NOT_FOUND)

views_list = PropertyView.objects.select_related("property", "state", "cycle").filter(
property__organization_id=org_id,
cycle__in=cycles,
property__access_level_instance__lft__gte=user_ali.lft,
property__access_level_instance__rgt__lte=user_ali.rgt,
)

views_list = views_list.annotate(**annotations).filter(filters).order_by("id")
values_list = ["id", "cycle_id"] # django readable names
returned_name = ["id", "cycle_id"] # actual api names
for column in columns:
if column.is_extra_data:
anno_value = F("state__extra_data__" + column.column_name)
elif column.derived_column:
anno_value = F("state__derived_data__" + column.column_name)
else:
anno_value = F("state__" + column.column_name)

name = f"{column.column_name.replace(' ', '_')}_{column.id}" # django readable name
annotations[name] = anno_value
values_list.append(name)
returned_name.append(f"{column.column_name}_{column.id}")

# use api names and add units
views_list = views_list.annotate(**annotations).values_list(*values_list)
views_list = [dict(zip(returned_name, view)) for view in views_list] # replace django readable name with api name
views_list = [apply_display_unit_preferences(org, view) for view in views_list]

return views_list

Expand Down

0 comments on commit 8adda7f

Please sign in to comment.