Skip to content

Commit

Permalink
Merge pull request #3240 from SEED-platform/3199-feat/add-espm-import…
Browse files Browse the repository at this point in the history
…-path

3199 feat/add espm import path
  • Loading branch information
Ryo committed May 12, 2022
2 parents e680eef + 9f6cdb9 commit 5dc3358
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 61 deletions.
Binary file modified locale/en_US/LC_MESSAGES/django.mo
Binary file not shown.
3 changes: 3 additions & 0 deletions locale/en_US/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -1673,6 +1673,9 @@ msgstr "Not all inventory items were successfully deleted"
msgid "Note:"
msgstr "Note:"

msgid "Note: Meters are labeled with the following format: \"Type - Source - Source ID\""
msgstr "Note: Meters are labeled with the following format: \"Type - Source - Source ID\""

msgid "Note: Parent organization members are not automatically made members of sub-organizations."
msgstr "Note: Parent organization members are not automatically made members of sub-organizations."

Expand Down
Binary file modified locale/fr_CA/LC_MESSAGES/django.mo
Binary file not shown.
3 changes: 3 additions & 0 deletions locale/fr_CA/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -1686,6 +1686,9 @@ msgstr "Tous les éléments de l'inventaire n'ont pas été supprimés"
msgid "Note:"
msgstr "Remarque:"

msgid "Note: Meters are labeled with the following format: \"Type - Source - Source ID\""
msgstr "Remarque: Les compteurs sont étiquetés au format suivant: \"la catégorie - Source - Source ID\""

msgid "Note: Parent organization members are not automatically made members of sub-organizations."
msgstr "Remarque: les membres de l'organisation parente ne sont pas automatiquement devenus membres de sous-organisations."

Expand Down
24 changes: 10 additions & 14 deletions seed/data_importer/meters_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
:copyright (c) 2014 - 2022, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Department of Energy) and contributors. All rights reserved.
:author
"""

import logging
import re
from calendar import monthrange
from datetime import datetime, timedelta
Expand All @@ -22,6 +22,8 @@
from seed.lib.superperms.orgs.models import Organization
from seed.models import Meter, PropertyState, PropertyView

_log = logging.getLogger(__name__)


class MetersParser(object):
"""
Expand Down Expand Up @@ -181,8 +183,6 @@ def proposed_imports(self):

if self._cache_proposed_imports is None:
self._cache_proposed_imports = []
energy_type_lookup = dict(Meter.ENERGY_TYPES)

# Gather info based on property_id - cycles (query) and related pm_property_ids (parsed in different method)
property_ids_info = {}
for pm_property_id, property_ids in self._source_to_property_ids.items():
Expand All @@ -200,7 +200,7 @@ def proposed_imports(self):
# Put summaries together based on source type
for meter in self.meter_and_reading_objs:
meter_summary = {
'type': energy_type_lookup[meter['type']],
'type': Meter.ENERGY_TYPE_BY_METER_TYPE[meter['type']],
'incoming': len(meter.get("readings")),
'property_id': meter['property_id'],
}
Expand Down Expand Up @@ -413,10 +413,9 @@ def validated_type_units(self):
these are deduplicated.
"""
type_unit_combinations = set()
energy_type_lookup = dict(Meter.ENERGY_TYPES)

for meter in self.meter_and_reading_objs:
type = energy_type_lookup[meter['type']]
type = Meter.ENERGY_TYPE_BY_METER_TYPE[meter['type']]
type_units = {
(type, reading['source_unit'])
for reading
Expand Down Expand Up @@ -450,18 +449,15 @@ class with the data.
# and natural gas in the same row)
provided_reading_types = []
for field in raw_data[0].keys():
if field.startswith('Electricity Use') or field.startswith('Natural Gas Use'):
provided_reading_types.append(field)
for header_string in Meter.ENERGY_TYPE_BY_HEADER_STRING.keys():
if field.startswith(header_string):
provided_reading_types.append(field)
continue

if not provided_reading_types:
return []

TYPE_AND_UNITS_REGEX = re.compile(r'(?P<meter_type>.*)\s+\((?P<units>.*)\)')
meter_type_lookup = dict(Meter.ENERGY_TYPES)
METER_TYPE_MAPPING = {
'Electricity Use (Grid)': meter_type_lookup[Meter.ELECTRICITY_GRID],
'Natural Gas Use': meter_type_lookup[Meter.NATURAL_GAS],
}
METER_UNITS_MAPPING = {
'kBtu': 'kBtu (thousand Btu)',
'GJ': 'GJ',
Expand Down Expand Up @@ -489,7 +485,7 @@ class with the data.
raise Exception(f'Failed to parse meter type and units from "{reading_type}"')

meter_type_match = type_and_units_match.group('meter_type').strip()
meter_type = METER_TYPE_MAPPING.get(meter_type_match)
meter_type = Meter.ENERGY_TYPE_BY_HEADER_STRING.get(meter_type_match)
if meter_type is None:
raise Exception(f'Invalid meter type "{meter_type_match}"')

Expand Down
34 changes: 34 additions & 0 deletions seed/models/meters.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,40 @@ class Meter(models.Model):
(COST, 'Cost'),
(ELECTRICITY_UNKNOWN, 'Electric - Unknown'),
)
ENERGY_TYPE_BY_METER_TYPE = dict(ENERGY_TYPES)

# list of header strings and their related energy types
ENERGY_TYPE_BY_HEADER_STRING = {

# these mappings are assumed based on ESPM values
'Coal Use (Anthracite)': ENERGY_TYPE_BY_METER_TYPE[COAL_ANTHRACITE],
'Coal Use (Bituminous)': ENERGY_TYPE_BY_METER_TYPE[COAL_BITUMINOUS],
'Coke': ENERGY_TYPE_BY_METER_TYPE[COKE],
'Diesel': ENERGY_TYPE_BY_METER_TYPE[DIESEL],
'District Chilled Water Use (Absorption)': ENERGY_TYPE_BY_METER_TYPE[DISTRICT_CHILLED_WATER_ABSORPTION],
'District Chilled Water Use (Electric)': ENERGY_TYPE_BY_METER_TYPE[DISTRICT_CHILLED_WATER_ELECTRIC],
'District Chilled Water Use (Engine)': ENERGY_TYPE_BY_METER_TYPE[DISTRICT_CHILLED_WATER_ENGINE],
'District Chilled Water Use (Other)': ENERGY_TYPE_BY_METER_TYPE[DISTRICT_CHILLED_WATER_OTHER],
'District Hot Water Use': ENERGY_TYPE_BY_METER_TYPE[DISTRICT_HOT_WATER],
'District Steam Use': ENERGY_TYPE_BY_METER_TYPE[DISTRICT_STEAM],
'Electricity Use (Grid)': ENERGY_TYPE_BY_METER_TYPE[ELECTRICITY_GRID],
'Electricity Use (Solar)': ENERGY_TYPE_BY_METER_TYPE[ELECTRICITY_SOLAR],
'Electricity Use (Wind)': ENERGY_TYPE_BY_METER_TYPE[ELECTRICITY_WIND],
'Fuel Oil Use (No. 1)': ENERGY_TYPE_BY_METER_TYPE[FUEL_OIL_NO_1],
'Fuel Oil Use (No. 2)': ENERGY_TYPE_BY_METER_TYPE[FUEL_OIL_NO_2],
'Fuel Oil Use (No. 4)': ENERGY_TYPE_BY_METER_TYPE[FUEL_OIL_NO_4],
'Fuel Oil Use (No. 5 and No. 6)': ENERGY_TYPE_BY_METER_TYPE[FUEL_OIL_NO_5_AND_NO_6],
'Kerosene Use': ENERGY_TYPE_BY_METER_TYPE[KEROSENE],
'Natural Gas Use': ENERGY_TYPE_BY_METER_TYPE[NATURAL_GAS],
'Other Use': ENERGY_TYPE_BY_METER_TYPE[OTHER],
'Propane Use': ENERGY_TYPE_BY_METER_TYPE[PROPANE],
'Wood Use': ENERGY_TYPE_BY_METER_TYPE[WOOD],
'Electricity Use (Unknown)': ENERGY_TYPE_BY_METER_TYPE[ELECTRICITY_UNKNOWN],

# theese values are added based on known usage
'Fuel Oil #2 Use': ENERGY_TYPE_BY_METER_TYPE[FUEL_OIL_NO_2],
'Diesel #2 Use': ENERGY_TYPE_BY_METER_TYPE[DIESEL],
}

type_lookup = dict((reversed(type) for type in ENERGY_TYPES)) # type: ignore

Expand Down
1 change: 1 addition & 0 deletions seed/static/seed/locales/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@
"Not Null": "Not Null",
"Not all inventory items were successfully deleted": "Not all inventory items were successfully deleted",
"Note:": "Note:",
"Note: Meters are labeled with the following format: \"Type - Source - Source ID\"": "Note: Meters are labeled with the following format: \"Type - Source - Source ID\"",
"Note: Parent organization members are not automatically made members of sub-organizations.": "Note: Parent organization members are not automatically made members of sub-organizations.",
"Note: Passwords must be 8 or more characters, and must contain at least 1 uppercase character, 1 lowercase character, and 1 digit.": "Note: Passwords must be 8 or more characters, and must contain at least 1 uppercase character, 1 lowercase character, and 1 digit.",
"Notes": "Notes",
Expand Down
1 change: 1 addition & 0 deletions seed/static/seed/locales/fr_CA.json
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@
"Not Null": "Pas nul",
"Not all inventory items were successfully deleted": "Tous les éléments de l'inventaire n'ont pas été supprimés",
"Note:": "Remarque:",
"Note: Meters are labeled with the following format: \"Type - Source - Source ID\"": "Remarque: Les compteurs sont étiquetés au format suivant: \"la catégorie - Source - Source ID\"",
"Note: Parent organization members are not automatically made members of sub-organizations.": "Remarque: les membres de l'organisation parente ne sont pas automatiquement devenus membres de sous-organisations.",
"Note: Passwords must be 8 or more characters, and must contain at least 1 uppercase character, 1 lowercase character, and 1 digit.": "Remarque: Les mots de passe doivent comporter 8 caractères ou plus et contenir au moins 1 caractère majuscule, 1 caractère minuscule et 1 chiffre.",
"Notes": "Remarques",
Expand Down
101 changes: 55 additions & 46 deletions seed/static/seed/partials/inventory_detail_meters.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,62 +31,71 @@ <h2>
</div>
</div>
</div>

<div class="section_content_container" ng-cloak>
<div class="inventory-list-controls">
<div class="form-group" style="margin-left: 3px;">
<label>{$:: 'Interval' | translate $}:</label>
<div style="display: inline-block;">
<div class="inventory-list-controls columns">

<div class="column">
<div class="row">
<label>Filter By:</label>
<select class="form-control"
ng-model="interval.selected"
ng-change="refresh_readings()"
ng-options="option for option in interval.options"
style="margin-left: 10px;">
ng-model="filterMethod.selected"
ng-change="applyFilters()"
ng-options="option for option in filterMethod.options">
</select>
</div>
<br><br>
<label>Filter By:</label>
<div style="display: inline-block;">
<div class="row">
<label>{$:: 'Interval' | translate $}:</label>
<select class="form-control"
ng-model="filterMethod.selected"
ng-change="applyFilters()"
ng-options="option for option in filterMethod.options"
style="margin-left: 10px;">
ng-model="interval.selected"
ng-change="refresh_readings()"
ng-options="option for option in interval.options">
</select>
</div>
<div class="row" ng-hide="filterMethod.selected !== 'meter'">
<div uib-dropdown is-open="meters_options.isopen" auto-close="outsideClick" on-toggle="meter_selection_toggled(open)">
<button type="button" class="btn btn-default" uib-dropdown-toggle>
Filter Meters <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu style="padding: 0px;">
<li ng-repeat="meter in meter_selections">
<div>
<label class="btn btn-default" style="width: 100%; display: flex; justify-content: space-between; border-radius: 0px;">
{$:: meter.label $}<input type="checkbox" ng-model="meter.selected" name="meter.label" style="width: 14px; height: 14px; margin-left: 10px;">
</label>
</div>
</li>
</ul>
</div>
</div>
<div class="row" ng-hide="filterMethod.selected !== 'scenario'">
<div uib-dropdown is-open="scenarios_options.isopen" auto-close="outsideClick" on-toggle="scenario_selection_toggled(open)">
<button type="button" class="btn btn-default" uib-dropdown-toggle>
Filter Scenarios <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu style="padding: 0px;">
<li ng-repeat="scenario in scenario_selections">
<div>
<label class="btn btn-default" style="width: 100%; display: flex; justify-content: space-between; border-radius: 0px;">
{$:: scenario.label $}<input type="checkbox" ng-model="scenario.selected" name="scenario.label" style="width: 14px; height: 14px; margin-left: 10px;">
</label>
</div>
</li>
</ul>
</div>
</div>
<div class="row">
<button type="button" ng-click="open_green_button_upload_modal()" class="btn btn-primary" translate>UPLOAD_GB_DATA_BUTTON</button>
</div>
</div>
<div class="form-group" uib-dropdown is-open="meters_options.isopen" auto-close="outsideClick" on-toggle="meter_selection_toggled(open)" style="margin-top: 15px;" ng-hide="filterMethod.selected !== 'meter'">
<button type="button" class="btn btn-default" uib-dropdown-toggle>
Filter Meters <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu style="padding: 0px;">
<li ng-repeat="meter in meter_selections">
<div>
<label class="btn btn-default" style="width: 100%; display: flex; justify-content: space-between; border-radius: 0px;">
{$:: meter.label $}<input type="checkbox" ng-model="meter.selected" name="meter.label" style="width: 14px; height: 14px; margin-left: 10px;">
</label>
</div>
</li>
</ul>
</div>
<div class="form-group" uib-dropdown is-open="scenarios_options.isopen" auto-close="outsideClick" on-toggle="scenario_selection_toggled(open)" style="margin-top: 15px;" ng-hide="filterMethod.selected !== 'scenario'">
<button type="button" class="btn btn-default" uib-dropdown-toggle>
Filter Scenarios <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu style="padding: 0px;">
<li ng-repeat="scenario in scenario_selections">
<div>
<label class="btn btn-default" style="width: 100%; display: flex; justify-content: space-between; border-radius: 0px;">
{$:: scenario.label $}<input type="checkbox" ng-model="scenario.selected" name="scenario.label" style="width: 14px; height: 14px; margin-left: 10px;">
</label>
</div>
</li>
</ul>
</div>
<div class="btn-group" style="padding-top: 15px;">
<button type="button" ng-click="open_green_button_upload_modal()" class="btn btn-primary" translate>UPLOAD_GB_DATA_BUTTON</button>

<div class="column grow">
<div class="text-info row pull-right" ng-hide="filterMethod.selected !== 'meter'">
<span translate>Note: Meters are labeled with the following format: "Type - Source - Source ID".</span>
</div>
</div>
</div>

</div>

<div class="section_content" ng-show="has_readings">
<div id="grid-container">
Expand Down
4 changes: 4 additions & 0 deletions seed/static/seed/scss/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2872,6 +2872,10 @@ gs-input .tags .tag-item .remove-button {
flex-grow: 1;
width: 100%;
}

.pull-right {
justify-content: end;
}
}

#filters-list,
Expand Down
4 changes: 3 additions & 1 deletion seed/utils/meters.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,11 @@ def _usages_by_month(self):
# partial usages of the full usage are calculated from a linear relationship between the range_seconds to the total_seconds
for range in ranges:
range_seconds = round((range[1] - range[0]).total_seconds())
if range_seconds == 0:
continue
month_key = range[1].strftime('%B %Y')
reading = usage['reading'] / total_seconds * range_seconds / conversion_factor
if reading:
if reading is not None:
monthly_readings[month_key] = monthly_readings.get(month_key, {'month': month_key})
monthly_readings[month_key][field_name] = round(monthly_readings[month_key].get(field_name, 0) + reading, 2)

Expand Down

0 comments on commit 5dc3358

Please sign in to comment.