Skip to content

Commit

Permalink
Merge branch 'main' into tutorial-review
Browse files Browse the repository at this point in the history
  • Loading branch information
nhoening committed Nov 8, 2023
2 parents 9df7cb6 + 39e0022 commit 3217a13
Show file tree
Hide file tree
Showing 23 changed files with 261 additions and 99 deletions.
2 changes: 1 addition & 1 deletion ci/update-packages.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#! /bin/bash

######################################################################
# This script sets up docker environments for supported Python versions
Expand Down
3 changes: 2 additions & 1 deletion documentation/api/change_log.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ API change log
v3.0-13 | 2023-10-31
""""""""""""""""""""

- Introduced a consultancy account. A consultancy account can have read access to multiple accounts that have its id in the `consultancy_account_id` column.
- Read access to accounts, assets and sensors is given to external consultants (users with the *consultant* role who belong to a different organisation account) in case a consultancy relationship has been set up.
- The `/accounts/<id>` (GET) endpoint includes the account ID of its consultancy.
- Introduced the ``site-consumption-capacity`` and ``site-production-capacity`` to the ``flex-context`` field for `/sensors/<id>/schedules/trigger` (POST).

v3.0-12 | 2023-09-20
Expand Down
19 changes: 17 additions & 2 deletions documentation/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,40 @@
FlexMeasures Changelog
**********************

v0.18.0 | December XX, 2023
============================

New features
-------------

Infrastructure / Support
----------------------

v0.17.0 | November XX, 2023
Bugfixes
-----------


v0.17.0 | November 8, 2023
============================

.. note:: Read more on these features on `the FlexMeasures blog <https://flexmeasures.io/017-consultancy/>`__.

.. warning:: Upgrading to this version requires running ``flexmeasures db upgrade`` (you can create a backup first with ``flexmeasures db-ops dump``).

New features
-------------

- Different site-level production and consumption limits can be defined for the storage scheduler via the API (``flex-context``) or via asset attributes [see `PR #884 <https://github.com/FlexMeasures/flexmeasures/pull/884>`_]
- Scheduling data better distinguishes (e.g. in chart tooltips) when a schedule was the result of a fallback mechanism, by splitting off the fallback mechanism from the main scheduler (as a separate job) [see `PR #846 <https://github.com/FlexMeasures/flexmeasures/pull/846>`_]
- A Consultancy account can be added to any account, with the effect that the customer-manager users in that account get read access. [see `PR #846 <https://github.com/FlexMeasures/flexmeasures/pull/877>`_]
- New accounts can set a consultancy relationship with another account to give read access to external consultants. [see `PR #877 <https://github.com/FlexMeasures/flexmeasures/pull/877>`_ and `PR #892 <https://github.com/FlexMeasures/flexmeasures/pull/892>`_]

Infrastructure / Support
----------------------

- Introduce a new one-to-many relation between assets, allowing the definition of an asset's parent (which is also an asset). This hierarchical relationship enables assets to be related in a structured manner. [see `PR #855 <https://github.com/FlexMeasures/flexmeasures/pull/855>`_ and `PR #874 <https://github.com/FlexMeasures/flexmeasures/pull/874>`_]
- Introduce a new format for the output of ``Scheduler`` to prepare for multiple outputs [see `PR #879 <https://github.com/FlexMeasures/flexmeasures/pull/879>`_].


v0.16.1 | October 2, 2023
============================

Expand Down
6 changes: 6 additions & 0 deletions documentation/cli/change_log.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@
FlexMeasures CLI Changelog
**********************

since v0.17.0 | November 8, 2023
=======================================

* Add ``--consultancy`` option to ``flexmeasures add account`` to create a consultancy relationship with another account.

since v0.16.0 | September 29, 2023
=======================================

* Add command ``flexmeasures add sources`` to add the base `DataSources` for the `DataGenerators`.
* Add command ``flexmeasures show chart`` to export sensor and asset charts in PNG or SVG formats.
* Add ``--kind reporter`` option to ``flexmeasures add toy-account`` to create the asset and sensors for the reporter tutorial.
Expand Down
12 changes: 6 additions & 6 deletions flexmeasures/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
from importlib_metadata import version, PackageNotFoundError

from flexmeasures.data.models.annotations import Annotation # noqa F401
from flexmeasures.data.models.user import ( # noqa F401
from flexmeasures.data.models.annotations import Annotation
from flexmeasures.data.models.user import (
Account,
AccountRole,
User,
Role as UserRole,
)
from flexmeasures.data.models.data_sources import DataSource as Source # noqa F401
from flexmeasures.data.models.generic_assets import ( # noqa F401
from flexmeasures.data.models.data_sources import DataSource as Source
from flexmeasures.data.models.generic_assets import (
GenericAsset as Asset,
GenericAssetType as AssetType,
)
from flexmeasures.data.models.planning import Scheduler # noqa F401
from flexmeasures.data.models.time_series import Sensor # noqa F401
from flexmeasures.data.models.planning import Scheduler
from flexmeasures.data.models.time_series import Sensor


__version__ = "Unknown"
Expand Down
12 changes: 5 additions & 7 deletions flexmeasures/api/v3_0/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ def index(self):
.. :quickref: Account; Download account list
This endpoint returns all accessible accounts.
Accessible accounts are your own account, the accounts you are a consultant for, or all accounts for admins.
When the super-account concept (GH#203) lands, then users in such accounts see all managed accounts.
Accessible accounts are your own account and accounts you are a consultant for, or all accounts for admins.
**Example response**
Expand Down Expand Up @@ -67,8 +66,8 @@ def index(self):
accounts = get_accounts()
else:
accounts = [current_user.account] + (
current_user.account.consultant_client_accounts
if "customer-manager" in current_user.roles
current_user.account.consultancy_client_accounts
if "consultant" in current_user.roles
else []
)

Expand All @@ -84,7 +83,7 @@ def get(self, id: int, account: Account):
.. :quickref: Account; Get an account
This endpoint retrieves an account, given its id.
Only admins, consultant users or the user themselves can use this endpoint.
Only admins, consultants and users belonging to the account itself can use this endpoint.
**Example response**
Expand All @@ -94,8 +93,7 @@ def get(self, id: int, account: Account):
'id': 1,
'name': 'Test Account'
'account_roles': [1, 3],
'consultancy_account_id':2,
'consultant_name':'Consultant',
'consultancy_account_id': 2,
}
:reqheader Authorization: The authentication token
Expand Down
14 changes: 14 additions & 0 deletions flexmeasures/api/v3_0/sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,11 +447,25 @@ def get_schedule( # noqa: C901

# Look up the scheduling job
connection = current_app.queues["scheduling"].connection

try: # First try the scheduling queue
job = Job.fetch(job_id, connection=connection)
except NoSuchJobError:
return unrecognized_event(job_id, "job")

if (
not current_app.config.get("FLEXMEASURES_FALLBACK_REDIRECT")
and job.is_failed
and (job.meta.get("fallback_job_id") is not None)
):
try: # First try the scheduling queue
job = Job.fetch(job.meta["fallback_job_id"], connection=connection)
except NoSuchJobError:
current_app.logger.error(
f"Fallback job with ID={job.meta['fallback_job_id']} (originator Job ID={job_id}) not found."
)
return unrecognized_event(job.meta["fallback_job_id"], "fallback-job")

scheduler_info_msg = ""
scheduler_info = job.meta.get("scheduler_info", dict(scheduler=""))
scheduler_info_msg = f"{scheduler_info['scheduler']} was used."
Expand Down
8 changes: 4 additions & 4 deletions flexmeasures/api/v3_0/tests/test_accounts_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ def test_get_accounts_missing_auth(client, requesting_user, status_code):
[
("test_admin_user@seita.nl", 7),
("test_prosumer_user@seita.nl", 1),
("test_consultant_user@seita.nl", 2),
("test_consultant_user_without_customer_manager_access@seita.nl", 1),
("test_consultant@seita.nl", 2),
("test_consultancy_user_without_consultant_access@seita.nl", 1),
],
indirect=["requesting_user"],
)
Expand All @@ -38,8 +38,8 @@ def test_get_accounts(client, setup_api_test_data, requesting_user, num_accounts
Get accounts for:
- A normal user.
- An admin user.
- A consultant account user with a customer-manager role and a linked consultant client account.
- A consultant account user without a customer-manager role.
- A user with a consultant role, belonging to a consultancy account with a linked consultancy client account.
- A user without a consultant role, belonging to a consultancy account with a linked consultancy client account.
"""
get_accounts_response = client.get(
url_for("AccountAPI:index"),
Expand Down
38 changes: 18 additions & 20 deletions flexmeasures/api/v3_0/tests/test_assets_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def test_get_asset_nonaccount_access(client, setup_api_test_data, requesting_use
[
("test_admin_user@seita.nl", "Prosumer", 1),
("test_admin_user@seita.nl", "Supplier", 2),
("test_consultant_user@seita.nl", "ConsultantClient", 1),
("test_consultant@seita.nl", "ConsultancyClient", 1),
],
indirect=["requesting_user"],
)
Expand Down Expand Up @@ -344,7 +344,7 @@ def test_delete_an_asset(client, setup_api_test_data, requesting_user):

@pytest.mark.parametrize(
"requesting_user",
["test_consultant_user@seita.nl"],
["test_consultant@seita.nl"],
indirect=True,
)
def test_consultant_can_read(
Expand All @@ -354,9 +354,9 @@ def test_consultant_can_read(
requesting_user,
):
"""
The Consultant Account reads the assets from the ConsultantClient Account.
The Consultant Account reads the assets from the ConsultancyClient Account.
"""
account_name = "ConsultantClient"
account_name = "ConsultancyClient"
query = {"account_id": setup_accounts[account_name].id}

get_assets_response = client.get(
Expand All @@ -366,29 +366,27 @@ def test_consultant_can_read(
print("Server responded with:\n%s" % get_assets_response.json)
assert get_assets_response.status_code == 200
assert len(get_assets_response.json) == 1
assert get_assets_response.json[0]["name"] == "Test ConsultantClient Asset"
assert get_assets_response.json[0]["name"] == "Test ConsultancyClient Asset"


@pytest.mark.parametrize(
"requesting_user", ["test_consultant_user@seita.nl"], indirect=True
)
@pytest.mark.parametrize("requesting_user", ["test_consultant@seita.nl"], indirect=True)
def test_consultant_can_not_patch(
client,
setup_api_test_data,
setup_accounts,
requesting_user,
):
"""
Try to edit an asset belonging to the ConsultantClient account with the Consultant account.
Try to edit an asset belonging to the ConsultancyClient account with the Consultant account.
The Consultant account only has read access.
"""
consultant_client_asset = GenericAsset.query.filter_by(
name="Test ConsultantClient Asset"
consultancy_client_asset = GenericAsset.query.filter_by(
name="Test ConsultancyClient Asset"
).one_or_none()
print(consultant_client_asset)
print(consultancy_client_asset)

asset_edit_response = client.patch(
url_for("AssetAPI:patch", id=consultant_client_asset.id),
url_for("AssetAPI:patch", id=consultancy_client_asset.id),
json={
"latitude": 0,
},
Expand All @@ -399,10 +397,10 @@ def test_consultant_can_not_patch(

@pytest.mark.parametrize(
"requesting_user",
["test_consultant_user_without_customer_manager_access@seita.nl"],
["test_consultancy_user_without_consultant_access@seita.nl"],
indirect=True,
)
def test_consultant_without_customer_manager_role(
def test_consultancy_user_without_consultant_role(
client,
setup_api_test_data,
setup_accounts,
Expand All @@ -411,7 +409,7 @@ def test_consultant_without_customer_manager_role(
"""
The Consultant Account user without customer manager role can not read.
"""
account_name = "ConsultantClient"
account_name = "ConsultancyClient"
query = {"account_id": setup_accounts[account_name].id}

get_assets_response = client.get(
Expand Down Expand Up @@ -480,7 +478,7 @@ def get_asset_with_name(asset_name):

@pytest.mark.parametrize(
"requesting_user",
["test_consultant_user@seita.nl"],
["test_consultant@seita.nl"],
indirect=True,
)
def test_consultant_get_asset(
Expand All @@ -490,15 +488,15 @@ def test_consultant_get_asset(
requesting_user,
):
"""
The Consultant Account reads an asset from the ConsultantClient Account.
The Consultant Account reads an asset from the ConsultancyClient Account.
"""
asset_id = (
GenericAsset.query.filter(GenericAsset.name == "Test ConsultantClient Asset")
GenericAsset.query.filter(GenericAsset.name == "Test ConsultancyClient Asset")
.one_or_none()
.id
)

get_asset_response = client.get(url_for("AssetAPI:get", id=asset_id))
print("Server responded with:\n%s" % get_asset_response.json)
assert get_asset_response.status_code == 200
assert get_asset_response.json["name"] == "Test ConsultantClient Asset"
assert get_asset_response.json["name"] == "Test ConsultancyClient Asset"
Loading

0 comments on commit 3217a13

Please sign in to comment.