Skip to content

Commit

Permalink
Merge branch 'develop' into 4789-import-labels
Browse files Browse the repository at this point in the history
  • Loading branch information
kflemin authored Jan 27, 2025
2 parents 6edc3a1 + 18b7bd2 commit d3edae1
Show file tree
Hide file tree
Showing 45 changed files with 367 additions and 124 deletions.
23 changes: 22 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
# SEED Version 3.2.3

<!-- Release notes generated using configuration in .github/release.yml at 3.2.3-release-prep -->

## What's Changed
### Improvements 📈
* Add meter of type Electric to BSyncR analysis by @kflemin in https://github.com/SEED-platform/seed/pull/4923
* Save org settings & 2fa prompt by @perryr16 in https://github.com/SEED-platform/seed/pull/4905
* Add Simple JWT to authorization setup by @crutan in https://github.com/SEED-platform/seed/pull/4926
* Portfolio Summary updates by @perryr16 in https://github.com/SEED-platform/seed/pull/4909
### Maintenance 🧹
* Remove inventory list `Data` nav item by @axelstudios in https://github.com/SEED-platform/seed/pull/4920
* Remove server settings from the session cookie by @axelstudios in https://github.com/SEED-platform/seed/pull/4927
### Bug Fixes 🐛
* Fix upgrade recommendation by @haneslinger in https://github.com/SEED-platform/seed/pull/4917
* Fix inventory groups add inventory bug by @perryr16 in https://github.com/SEED-platform/seed/pull/4932
* Show default display name on analyses by @perryr16 in https://github.com/SEED-platform/seed/pull/4924


**Full Changelog**: https://github.com/SEED-platform/seed/compare/v3.2.2...v3.2.3

# SEED Version 3.2.2

<!-- Release notes generated using configuration in .github/release.yml at 3.2.2-release-prep -->
Expand Down Expand Up @@ -2385,7 +2406,7 @@ Accepted External Pull Requests: 11

# SEED Version 2.0.0 (2016-06-11 to 2016-10-01)

losed Issues: 21
Closed Issues: 21
- Fixed [#30]( https://github.com/SEED-platform/seed/issues/30 ), Multiple Data Files per Building Record
- Fixed [#59]( https://github.com/SEED-platform/seed/issues/59 ), Column Reordering allowed in Matching Edit Columns view
- Fixed [#66]( https://github.com/SEED-platform/seed/issues/66 ), Add Ability to handle multiple years of data
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SEED Platform™, Copyright (c) 2017, 2024 Alliance for Sustainable Energy, LLC, and other contributors.
SEED Platform™, Copyright (c) 2017, 2025 Alliance for Sustainable Energy, LLC, and other contributors.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted
Expand Down
10 changes: 10 additions & 0 deletions config/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""

import os
from datetime import timedelta
from distutils.util import strtobool

from django.utils.translation import gettext_lazy as _
Expand Down Expand Up @@ -101,6 +102,7 @@
"two_factor.plugins.phonenumber", # <- if you want phone number capability.
"two_factor.plugins.email", # <- if you want email capability.
# "two_factor.plugins.yubikey", # <- for yubikey capability.
"rest_framework_simplejwt",
)


Expand Down Expand Up @@ -268,6 +270,7 @@
# Django Rest Framework
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
"rest_framework.authentication.SessionAuthentication",
"seed.authentication.SEEDAuthentication",
),
Expand Down Expand Up @@ -304,6 +307,13 @@
"LOGOUT_URL": "/accounts/logout",
}

SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=5),
"AUTH_HEADER_TYPES": ("Bearer",),
"TOKEN_OBTAIN_SERIALIZER": "seed.landing.serializers.SeedTokenObtainPairSerializer",
"ROTATE_REFRESH_TOKENS": True,
}

try:
EEEJ_LOAD_SMALL_TEST_DATASET = bool(strtobool(os.environ.get("EEEJ_LOAD_SMALL_TEST_DATASET", "False")))
except Exception:
Expand Down
9 changes: 7 additions & 2 deletions config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from rest_framework import permissions
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from two_factor.urls import urlpatterns as tf_urls

from config.views import robots_txt
from seed.api.base.urls import urlpatterns as api
from seed.landing.views import CustomLoginView, password_reset_complete, password_reset_confirm, password_reset_done
from seed.views.main import angular_js_tests, health_check, version
from seed.views.main import angular_js_tests, config, health_check, version

schema_view = get_schema_view(
openapi.Info(
Expand Down Expand Up @@ -51,9 +52,13 @@ def trigger_error(request):
# root configuration items
re_path(r"^i18n/", include("django.conf.urls.i18n")),
re_path(r"^robots\.txt", robots_txt, name="robots_txt"),
# API
# API (explicit no-auth)
re_path(r"^api/config/$", config, name="config"),
re_path(r"^api/health_check/$", health_check, name="health_check"),
# API
re_path(r"^api/swagger/$", schema_view.with_ui("swagger", cache_timeout=0), name="schema-swagger-ui"),
re_path(r"^api/token/$", TokenObtainPairView.as_view(), name="token_obtain_pair"),
re_path(r"^api/token/refresh/$", TokenRefreshView.as_view(), name="token_refresh"),
re_path(r"^api/version/$", version, name="version"),
re_path(r"^api/", include((api, "seed"), namespace="api")),
re_path(r"^account/login", CustomLoginView.as_view(), name="login"),
Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@

# General information about the project.
project = "SEED Platform"
copyright = "2017, 2024, Alliance for Sustainable Energy, LLC, and other contributors."
copyright = "2017, 2025, Alliance for Sustainable Energy, LLC, and other contributors."
author = "Alliance for Sustainable Energy, LLC, and other contributors."

# The version info for the project you're documenting, acts as replacement for
Expand Down
4 changes: 4 additions & 0 deletions docs/source/migrations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ local_untracked.py file
),
)
Version 3.2.3
-------------
- There are no special migrations needed for this version. Simply run ``./manage.py migrate``.

Version 3.2.2
-------------
- There are no special migrations needed for this version. Simply run ``./manage.py migrate``.
Expand Down
Binary file modified locale/en_US/LC_MESSAGES/django.mo
Binary file not shown.
12 changes: 12 additions & 0 deletions locale/en_US/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,12 @@ msgstr "Advanced Settings"
msgid "Alias"
msgstr "Alias"

msgid "All Canonical Fields"
msgstr "All Canonical Fields"

msgid "All Extra Data Fields"
msgstr "All Extra Data Fields"

msgid "An Audit Template organization token, user email and password are required"
msgstr "An Audit Template organization token, user email and password are required"

Expand Down Expand Up @@ -970,6 +976,12 @@ msgstr "Create a New Sub-Organization"
msgid "Create a Program to get started!"
msgstr "Create a Program to get started!"

msgid "Create a Property"
msgstr "Create a Property"

msgid "Create a Tax Lot"
msgstr "Create a Tax Lot"

msgid "Create a new rule"
msgstr "Create a new rule"

Expand Down
Binary file modified locale/es/LC_MESSAGES/django.mo
Binary file not shown.
14 changes: 13 additions & 1 deletion locale/es/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: lokalise.com\n"
"Project-Id-Version: SEED Platform\n"
"PO-Revision-Date: 2024-12-14 00:26\n"
"PO-Revision-Date: 2025-01-24 01:19\n"
"Last-Translator: lokalise.com\n"
"Language-Team: lokalise.com\n\n"
"Language: es\n"
Expand Down Expand Up @@ -461,6 +461,12 @@ msgstr "Configuración avanzada"
msgid "Alias"
msgstr "Alias"

msgid "All Canonical Fields"
msgstr "Todos los campos canónicos"

msgid "All Extra Data Fields"
msgstr "Todos los campos de datos adicionales"

#, fuzzy
msgid "An Audit Template organization token, user email and password are required"
msgstr "Se requiere un token de organización de Plantilla de Auditoría, un correo electrónico de usuario y una contraseña"
Expand Down Expand Up @@ -1245,6 +1251,12 @@ msgstr "Crear una nueva suborganización"
msgid "Create a Program to get started!"
msgstr "Cree un programa para empezar"

msgid "Create a Property"
msgstr "Crear una propiedad"

msgid "Create a Tax Lot"
msgstr "Crear un lote de impuestos"

#, fuzzy
msgid "Create a new rule"
msgstr "Crear una nueva regla"
Expand Down
Binary file modified locale/fr_CA/LC_MESSAGES/django.mo
Binary file not shown.
12 changes: 12 additions & 0 deletions locale/fr_CA/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,12 @@ msgstr "Réglages avancés"
msgid "Alias"
msgstr "Alias"

msgid "All Canonical Fields"
msgstr "Tous les champs canoniques"

msgid "All Extra Data Fields"
msgstr "Tous les champs de données supplémentaires"

msgid "An Audit Template organization token, user email and password are required"
msgstr "Un jeton d'organisation Audit Template, un e-mail d'utilisateur et un mot de passe sont requis"

Expand Down Expand Up @@ -980,6 +986,12 @@ msgstr "Créer une nouvelle sous-organisation"
msgid "Create a Program to get started!"
msgstr "Créez un programme pour commencer !"

msgid "Create a Property"
msgstr "Créer une propriété"

msgid "Create a Tax Lot"
msgstr "Créer un lot fiscal"

msgid "Create a new rule"
msgstr "Créer une nouvelle règle"

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "seed",
"version": "3.2.2",
"version": "3.2.3",
"description": "Standard Energy Efficiency Data (SEED) Platform™",
"license": "SEE LICENSE IN LICENSE.md",
"directories": {
Expand Down
1 change: 1 addition & 0 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ django-pint==0.7.3

# API
djangorestframework==3.15.1 # Update after Django 4.2
djangorestframework-simplejwt==5.3.1 # Update after Django 4.2
django-post_office==3.8.0 # Update after Django 4.2
drf-yasg==1.21.7
django-filter==22.1 # Update after Django 4.2 and drf-spectacular
Expand Down
8 changes: 7 additions & 1 deletion seed/analysis_pipelines/bsyncr.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,13 @@ def _prepare_all_properties(self, analysis_view_ids_by_property_view_id, analysi
for analysis_property_view in analysis_property_views:
meters = Meter.objects.annotate(readings_count=Count("meter_readings")).filter(
property=analysis_property_view.property,
type__in=[Meter.ELECTRICITY_GRID, Meter.ELECTRICITY_SOLAR, Meter.ELECTRICITY_WIND, Meter.ELECTRICITY_UNKNOWN],
type__in=[
Meter.ELECTRICITY,
Meter.ELECTRICITY_GRID,
Meter.ELECTRICITY_SOLAR,
Meter.ELECTRICITY_WIND,
Meter.ELECTRICITY_UNKNOWN,
],
readings_count__gte=12,
)
if meters.count() == 0:
Expand Down
8 changes: 4 additions & 4 deletions seed/analysis_pipelines/upgrade_recommendation.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def get_value(name):

# check if this is a pint, if so get value
if isinstance(gross_floor_area, Quantity):
gross_floor_area = gross_floor_area.to_base_units().magnitude
gross_floor_area = gross_floor_area.magnitude

# calc eui
if total_eui:
Expand All @@ -119,7 +119,7 @@ def get_value(name):
if target_gas_eui is None or target_electric_eui is None:
return "Missing Data (ASHRAE Target Gas EUI/ASHRAE Target Electric EUI)"
else:
benchmark = (float(target_gas_eui) + float(target_electric_eui)) / 0.8
total_target_eui = float(target_gas_eui) + float(target_electric_eui)

# if young building:
retrofit_threshold_year = config.get("year_built_threshold")
Expand All @@ -134,7 +134,7 @@ def get_value(name):
# has_bas = ddc_control_panel_count.order_by("ddc_control_panel_count").first().ddc_control_panel_count > 0

fair_actual_to_benchmark_eui_ratio = config.get("fair_actual_to_benchmark_eui_ratio")
if ((eui / benchmark) > fair_actual_to_benchmark_eui_ratio) and has_bas is True:
if ((eui / total_target_eui) > fair_actual_to_benchmark_eui_ratio) and has_bas is True:
return "Re-tuning"
else:
return "NO DER project recommended"
Expand All @@ -146,7 +146,7 @@ def get_value(name):
# if big and actual to benchmark eui ratio is "poor"
poor_actual_to_benchmark_eui_ratio = config.get("poor_actual_to_benchmark_eui_ratio")
building_sqft_threshold = config.get("building_sqft_threshold")
if ((eui / benchmark) > poor_actual_to_benchmark_eui_ratio) and gross_floor_area > building_sqft_threshold:
if ((eui / total_target_eui) > poor_actual_to_benchmark_eui_ratio) and gross_floor_area > building_sqft_threshold:
return "Deep Energy Retrofit"

# for this next step, we will need condition_index
Expand Down
14 changes: 0 additions & 14 deletions seed/landing/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,10 @@

from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.utils.translation import gettext_lazy as _

from seed.landing.models import SEEDUser


class LoginForm(forms.Form):
email = forms.EmailField(
label=_("Email"),
help_text=_("ex: [email protected]"),
widget=forms.TextInput(attrs={"class": "field", "placeholder": _("Email Address")}),
)
password = forms.CharField(
label=_("Password"),
widget=forms.PasswordInput(attrs={"class": "field", "placeholder": _("Password"), "autocomplete": "off"}),
required=True,
)


class CustomCreateUserForm(UserCreationForm):
class Meta:
model = SEEDUser
Expand Down
36 changes: 22 additions & 14 deletions seed/landing/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from rest_framework import exceptions
from rest_framework_simplejwt.exceptions import TokenError
from rest_framework_simplejwt.tokens import AccessToken

from seed.lib.superperms.orgs.models import Organization

Expand Down Expand Up @@ -82,23 +84,29 @@ def process_header_request(cls, request):
return None

try:
if not auth_header.startswith("Basic"):
raise exceptions.AuthenticationFailed("Only Basic HTTP_AUTHORIZATION is supported")

auth_header = auth_header.split()[1]
auth_header = base64.urlsafe_b64decode(auth_header).decode("utf-8")
username, api_key = auth_header.split(":")

valid_api_key = re.search("^[a-f0-9]{40}$", api_key)
if not valid_api_key:
raise exceptions.AuthenticationFailed("Invalid API key")

user = SEEDUser.objects.get(api_key=api_key, username=username)
return user
if auth_header.startswith("Basic"):
auth_header = auth_header.split()[1]
auth_header = base64.urlsafe_b64decode(auth_header).decode("utf-8")
username, api_key = auth_header.split(":")

valid_api_key = re.search("^[a-f0-9]{40}$", api_key)
if not valid_api_key:
raise exceptions.AuthenticationFailed("Invalid API key")

user = SEEDUser.objects.get(api_key=api_key, username=username)
return user
elif auth_header.startswith("Bearer"):
at = AccessToken(auth_header.removeprefix("Bearer "))
user = SEEDUser.objects.get(pk=at["user_id"])
return user
else:
raise exceptions.AuthenticationFailed("Only Basic HTTP_AUTHORIZATION or BEARER Tokens are supported")
except ValueError:
raise exceptions.AuthenticationFailed("Invalid HTTP_AUTHORIZATION Header")
except TokenError:
raise exceptions.AuthenticationFailed("Invalid Bearer Token")
except SEEDUser.DoesNotExist:
raise exceptions.AuthenticationFailed("Invalid API key")
raise exceptions.AuthenticationFailed("Invalid API key or Bearer Token")

def get_absolute_url(self):
return f"/users/{quote(self.username)}/"
Expand Down
14 changes: 14 additions & 0 deletions seed/landing/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer


class SeedTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super().get_token(user)

# Add custom claims
token["name"] = f"{user.first_name} {user.last_name}".strip()
token["username"] = user.username
token["email"] = user.email

return token
Loading

0 comments on commit d3edae1

Please sign in to comment.