Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fea09c6
start of org stats
kflemin Feb 24, 2025
ec09268
statistics model
kflemin Feb 25, 2025
aba9984
save statistics setup
kflemin Feb 25, 2025
ffb9954
remove console logs
kflemin Feb 25, 2025
df0fc0e
first pass org stats page
kflemin Mar 3, 2025
e123c9c
XML report updates for AT export
kflemin Jul 17, 2025
35ba6cc
merge
kflemin Jul 31, 2025
1579775
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Aug 5, 2025
36c8e41
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Aug 6, 2025
ff67e51
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Aug 11, 2025
982e210
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Aug 14, 2025
8439912
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Aug 15, 2025
7382f59
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Aug 22, 2025
59f2471
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Aug 26, 2025
7a756b9
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Sep 3, 2025
cb35ee3
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Sep 15, 2025
36952aa
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Sep 18, 2025
f089a50
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Sep 19, 2025
3455232
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Sep 23, 2025
8656fb9
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Sep 29, 2025
8192591
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Oct 8, 2025
cddb586
Merge branch 'develop' of https://github.com/SEED-platform/seed into …
kflemin Oct 10, 2025
7cb3097
merge develop branch
kflemin Oct 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions seed/api/v3/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
from seed.views.v3.salesforce_mappings import SalesforceMappingViewSet
from seed.views.v3.sensors import SensorViewSet
from seed.views.v3.services import ServiceViewSet
from seed.views.v3.statistics import StastisticsSetupViewSet
from seed.views.v3.systems import SystemViewSet
from seed.views.v3.tax_lot_properties import TaxLotPropertyViewSet
from seed.views.v3.taxlot_views import TaxlotViewViewSet
Expand Down Expand Up @@ -120,6 +121,7 @@
api_v3_router.register(r"report_configurations", ReportConfigurationViewSet, basename="report_configurations")
api_v3_router.register(r"salesforce_configs", SalesforceConfigViewSet, basename="salesforce_configs")
api_v3_router.register(r"salesforce_mappings", SalesforceMappingViewSet, basename="salesforce_mappings")
api_v3_router.register(r"statistics", StastisticsSetupViewSet, basename="statistics")
api_v3_router.register(r"tax_lot_properties", TaxLotPropertyViewSet, basename="tax_lot_properties")
api_v3_router.register(r"taxlot_views", TaxlotViewViewSet, basename="taxlot_views")
api_v3_router.register(r"taxlots", TaxlotViewSet, basename="taxlots")
Expand Down
104 changes: 104 additions & 0 deletions seed/migrations/0243_statisticssetup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Generated by Django 3.2.25 on 2025-02-27 20:06

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("orgs", "0041_add_at_tracking_fields"),
("seed", "0242_add_meter_types"),
]

operations = [
migrations.CreateModel(
name="StatisticsSetup",
fields=[
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("gfa_units", models.CharField(choices=[("ft2", "ft2"), ("m2", ",2")], default="ft2", max_length=20)),
(
"electricity_units",
models.CharField(
choices=[("GJ", "GJ"), ("kBtu", "kBtu"), ("kWh", "kWh"), ("MBtu/MMBtu", "MBtu/MMBtu"), ("MWh", "MWh")],
default=None,
max_length=50,
),
),
(
"natural_gas_units",
models.CharField(
choices=[
("GJ", "GJ"),
("kBtu", "kBtu"),
("MBtu/MMBtu", "MBtu/MMBtu"),
("therms", "therms"),
("kWh", "kWh"),
("kcf", "kcf"),
("Mcf", "Mcf"),
],
default=None,
max_length=50,
),
),
(
"electricity_eui_units",
models.CharField(
choices=[
("kBtu/ft2", "kBtu/ft2"),
("kBtu/m2", "kBtu/m2"),
("kWh/ft2", "kWh/ft2"),
("kWh/m2", "kWh/m2"),
("GJ/m2", "GJ/m2"),
("GJ/ft2", "GJ/ft2"),
],
default="kBtu/ft2",
max_length=50,
),
),
(
"natural_gas_eui_units",
models.CharField(
choices=[
("kBtu/ft2", "kBtu/ft2"),
("kBtu/m2", "kBtu/m2"),
("kWh/ft2", "kWh/ft2"),
("kWh/m2", "kWh/m2"),
("GJ/m2", "GJ/m2"),
("GJ/ft2", "GJ/ft2"),
],
default="kBtu/ft2",
max_length=50,
),
),
(
"electricity_column",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="electricity_column", to="seed.column"
),
),
(
"electricity_eui_column",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="electricity_eui_column", to="seed.column"
),
),
(
"gfa_column",
models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="gfa_column", to="seed.column"),
),
(
"natural_gas_column",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="natural_gas_column", to="seed.column"
),
),
(
"natural_gas_eui_column",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="natural_gas_eui_column", to="seed.column"
),
),
("organization", models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to="orgs.organization")),
],
),
]
1 change: 1 addition & 0 deletions seed/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from .salesforce_mappings import *
from .sensors import *
from .simulations import *
from .statistics_setups import *
from .building_file import *
from .inventory_document import *
from .inventory_groups import *
Expand Down
64 changes: 64 additions & 0 deletions seed/models/statistics_setups.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""
SEED Platform (TM), Copyright (c) Alliance for Sustainable Energy, LLC, and other contributors.
See also https://github.com/SEED-platform/seed/blob/main/LICENSE.md
"""

import logging

from django.db import models

from seed.data_importer.utils import kbtu_thermal_conversion_factors
from seed.lib.superperms.orgs.models import Organization
from seed.models.columns import Column

logger = logging.getLogger(__name__)


class StatisticsSetup(models.Model):
# Stores all the configuration needed to calculate organization statistics
# Retrieve default values
# find the kbtu_thermal_conversion_factors entry under Electric that has "kWh" in it
electric_factors = kbtu_thermal_conversion_factors("US").get("Electric", {})
electric_default = next((item for item in electric_factors.items() if "kWh" in item), None)
# find the value under "Natural Gas" that has "therms" in it
gas_factors = kbtu_thermal_conversion_factors("US").get("Natural Gas", {})
gas_default = next((item for item in electric_factors.items() if "therms" in item), None)
# set area default to 'ft2'
area_units_default = "ft2"
AREA_UNITS = (
("ft2", "ft2"),
("m2", ",2"),
)

ELECTRIC_UNITS = (("GJ", "GJ"), ("kBtu", "kBtu"), ("kWh", "kWh"), ("MBtu/MMBtu", "MBtu/MMBtu"), ("MWh", "MWh"))

EUI_UNITS = (
("kBtu/ft2", "kBtu/ft2"),
("kBtu/m2", "kBtu/m2"),
("kWh/ft2", "kWh/ft2"),
("kWh/m2", "kWh/m2"),
("GJ/m2", "GJ/m2"),
("GJ/ft2", "GJ/ft2"),
)

GAS_UNITS = (
("GJ", "GJ"),
("kBtu", "kBtu"),
("MBtu/MMBtu", "MBtu/MMBtu"),
("therms", "therms"),
("kWh", "kWh"),
("kcf", "kcf"),
("Mcf", "Mcf"),
)

organization = models.OneToOneField(Organization, on_delete=models.CASCADE)
gfa_column = models.ForeignKey(Column, related_name="gfa_column", null=True, on_delete=models.SET_NULL)
gfa_units = models.CharField(max_length=20, choices=AREA_UNITS, default=area_units_default)
electricity_column = models.ForeignKey(Column, related_name="electricity_column", null=True, on_delete=models.SET_NULL)
electricity_units = models.CharField(max_length=50, choices=ELECTRIC_UNITS, default=electric_default)
natural_gas_column = models.ForeignKey(Column, related_name="natural_gas_column", null=True, on_delete=models.SET_NULL)
natural_gas_units = models.CharField(max_length=50, choices=GAS_UNITS, default=gas_default)
electricity_eui_column = models.ForeignKey(Column, related_name="electricity_eui_column", null=True, on_delete=models.SET_NULL)
electricity_eui_units = models.CharField(max_length=50, choices=EUI_UNITS, default="kBtu/ft2")
natural_gas_eui_column = models.ForeignKey(Column, related_name="natural_gas_eui_column", null=True, on_delete=models.SET_NULL)
natural_gas_eui_units = models.CharField(max_length=50, choices=EUI_UNITS, default="kBtu/ft2")
29 changes: 29 additions & 0 deletions seed/serializers/statistics_setups.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""
SEED Platform (TM), Copyright (c) Alliance for Sustainable Energy, LLC, and other contributors.
See also https://github.com/SEED-platform/seed/blob/main/LICENSE.md
"""

from rest_framework import serializers

from seed.models.statistics_setups import StatisticsSetup


class StatisticsSetupSerializer(serializers.ModelSerializer):
organization_id = serializers.IntegerField(required=True)

class Meta:
model = StatisticsSetup
fields = (
"id",
"organization_id",
"gfa_column",
"gfa_units",
"electricity_column",
"electricity_units",
"natural_gas_column",
"natural_gas_units",
"electricity_eui_column",
"electricity_eui_units",
"natural_gas_eui_column",
"natural_gas_eui_units",
)
169 changes: 169 additions & 0 deletions seed/static/seed/js/controllers/organization_stats_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/**
* SEED Platform (TM), Copyright (c) Alliance for Sustainable Energy, LLC, and other contributors.
* See also https://github.com/SEED-platform/seed/blob/main/LICENSE.md
*/
angular.module('SEED.controller.organization_stats', []).controller('organization_stats_controller', [
'$scope',
'all_columns',
'organization_payload',
'auth_payload',
'statistics_payload',
'statistics_service',
'Notification',

// eslint-disable-next-line func-names
function ($scope, all_columns, organization_payload, auth_payload, statistics_payload, statistics_service, Notification) {
$scope.fields = all_columns.columns;
$scope.org = organization_payload.organization;
$scope.auth = auth_payload.auth;
$scope.data_fetched = false;
$scope.data = [];

$scope.conf = {};
if (statistics_payload.length > 0) {
$scope.conf = statistics_payload[0];
}

// hardcoding these for now but we should make a service for them at some point
$scope.electric_units = [
'GJ',
'kBtu',
'kWh',
'MBtu/MMBtu',
'MWh'
];
$scope.gas_units = [
'GJ',
'kBtu',
'MBtu/MMBtu',
'therms',
'kWh',
'kcf',
'Mcf'
];
$scope.area_units = ['ft2', 'm2'];

$scope.eui_units = [
'kBtu/ft2',
'kBtu/m2',
'kWh/ft2',
'kWh/m2',
'GJ/m2',
'GJ/ft2'
];

$scope.btnText = 'Expand Configurations';
$scope.changeText = (btnText) => {
if (btnText === 'Collapse Configurations') {
$scope.btnText = 'Expand Configurations';
} else {
$scope.btnText = 'Collapse Configurations';
}
};

/* save settings */
$scope.save_settings = () => {
$scope.settings_updated = false;
if ($scope.conf.id) {
// update
statistics_service
.update_statistic($scope.org.id, $scope.conf.id, $scope.conf)
.then((response) => {
if (response.status === 'error') {
$scope.config_errors = response.errors;
} else {
statistics_service.get_statistics($scope.org.id).then((data) => {
$scope.conf = data.length > 0 ? data[0] : {};
});
$scope.settings_updated = true;
}
})
.catch((response) => {
if (response.data && response.data.status === 'error') {
$scope.config_errors = response.data.message;
} else {
$scope.config_errors = 'An unknown error has occurred';
}
Notification.error({ message: `Error: ${$scope.config_errors}`, delay: 15000, closeOnClick: true });
});
} else {
// create
statistics_service
.new_statistic($scope.org.id, $scope.conf)
.then(() => {
statistics_service.get_statistics($scope.org.id).then((data) => {
$scope.conf = data.length > 0 ? data[0] : {};
});
$scope.settings_updated = true;
})
.catch((response) => {
if (response.data && response.data.status === 'error') {
$scope.config_errors = response.data.message;
} else {
$scope.config_errors = 'An unknown error has occurred';
}
Notification.error({ message: `Error: ${$scope.config_errors}`, delay: 15000, closeOnClick: true });
});
}
};

/* calculate stats and update data display */
$scope.calculate = () => {
// calculate statistics
statistics_service
.calculate_statistics($scope.org.id, $scope.conf.id)
.then((response) => {
if (response.status === 'error') {
$scope.config_errors = response.errors;
} else {
// fetched data
$scope.data = response.data.data;
// $scope.boxplotData = {};
// $scope.elec_chart = $scope.create_chart('canvas_elec', $scope.data.chart_data_elec, 'Electricity Energy Use Intensity');
// $scope.gas_chart = $scope.create_chart('canvas_gas', $scope.data.chart_data_gas, 'Natural Gas Energy Use Intensity');
$scope.data_fetched = true;
}
})
.catch((response) => {
if (response.data && response.status === 'error') {
$scope.config_errors = response.data.message;
} else {
$scope.config_errors = 'An unknown error has occurred';
}
Notification.error({ message: `Error: ${$scope.config_errors}`, delay: 15000, closeOnClick: true });
});
};

// $scope.create_chart = (chartType, data, title_text) => {
// $scope.boxplotData[chartType] = {
// // define label tree
// // labels are the cycle in $scope.data
// labels: Object.keys(data),
// datasets: []
// };
// // iterate over keys of $scope.chart_data_elec hash and add data to datasets
// for (const key in data) {
// $scope.boxplotData[chartType].datasets.push({
// label: key,
// data: data[key]
// });
// }
// const canvas = document.getElementById(chartType);
// const ctx = canvas.getContext('2d');
// return new Chart(ctx, {
// type: 'boxplot',
// data: $scope.boxplotData[chartType],
// options: {
// responsive: true,
// legend: {
// position: 'top'
// },
// title: {
// display: true,
// text: title_text
// }
// }
// });
// };
}
]);
Loading
Loading