Skip to content

Commit

Permalink
Add flexmeasures show CLI commands (#339)
Browse files Browse the repository at this point in the history
* Add flexmeasures show CLI commands

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* list new commands in CLI reference

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* don't expose the 'generic' term to users

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* add show roles and show asset-types, update main changelog

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* also list data sources and order everything by ID

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* order lists by name

Signed-off-by: Nicolas Höning <nicolas@seita.nl>
  • Loading branch information
nhoening committed Jan 29, 2022
1 parent 3c6d3a7 commit 43a0173
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 1 deletion.
2 changes: 2 additions & 0 deletions documentation/changelog.rst
Expand Up @@ -11,6 +11,8 @@ New features

* Add CLI-commands ``flexmeasures add sensor``, ``flexmeasures add asset-type``, ``flexmeasures add beliefs`` (which were experimental features before). [see `PR #337 <http://www.github.com/FlexMeasures/flexmeasures/pull/337>`_]

* add CLI comands for showing data [see `PR #339 <http://www.github.com/FlexMeasures/flexmeasures/pull/339>`_]

Bugfixes
-----------

Expand Down
5 changes: 5 additions & 0 deletions documentation/cli/change_log.rst
Expand Up @@ -4,6 +4,11 @@
FlexMeasures CLI Changelog
**********************

since v0.9.0 | January 26, 2022
=====================

* add CLI comands for showing data ``flexmeasures show accounts``, ``flexmeasures show account``, ``flexmeasures show roles``, ``flexmeasures show asset-types``, ``flexmeasures show asset`` and ``flexmeasures show data-sources``.


since v0.9.0 | January 26, 2022
=====================
Expand Down
13 changes: 13 additions & 0 deletions documentation/cli/commands.rst
Expand Up @@ -38,6 +38,19 @@ of which some are referred to in this documentation.
================================================= =======================================


``show`` - Show data
--------------

================================================= =======================================
``flexmeasures show accounts`` List accounts.
``flexmeasures show account`` Show an account, its users and assets.
``flexmeasures show asset-types` List available asset types.
``flexmeasures show asset`` Show an asset and its sensors.
``flexmeasures show roles`` List available account- and user roles.
``flexmeasures show data-sources`` List available data sources.
================================================= =======================================


``delete`` - Delete data
--------------

Expand Down
1 change: 1 addition & 0 deletions flexmeasures/cli/__init__.py
Expand Up @@ -8,6 +8,7 @@ def register_at(app: Flask):
import flexmeasures.cli.jobs
import flexmeasures.cli.monitor
import flexmeasures.cli.data_add
import flexmeasures.cli.data_show
import flexmeasures.cli.data_delete
import flexmeasures.cli.db_ops
import flexmeasures.cli.testing # noqa: F401
2 changes: 1 addition & 1 deletion flexmeasures/cli/data_add.py
@@ -1,4 +1,4 @@
"""CLI Tasks for (de)populating the database - most useful in development"""
"""CLI Tasks for populating the database - most useful in development"""

from datetime import timedelta
from typing import Dict, List, Optional
Expand Down
220 changes: 220 additions & 0 deletions flexmeasures/cli/data_show.py
@@ -0,0 +1,220 @@
"""CLI Tasks for listing database contents - most useful in development"""

import click
from flask import current_app as app
from flask.cli import with_appcontext
from tabulate import tabulate

from flexmeasures.data.models.user import Account, AccountRole, User, Role
from flexmeasures.data.models.data_sources import DataSource
from flexmeasures.data.models.generic_assets import GenericAsset, GenericAssetType
from flexmeasures.data.models.time_series import Sensor


@click.group("show")
def fm_show_data():
"""FlexMeasures: Show data."""


@fm_show_data.command("accounts")
@with_appcontext
def list_accounts():
"""
List all accounts on this FlexMeasures instance.
"""
accounts = Account.query.order_by(Account.name).all()
if not accounts:
click.echo("No accounts created yet.")
return
click.echo("All accounts on this FlexMeasures instance:\n ")
account_data = [
(
account.id,
account.name,
GenericAsset.query.filter(GenericAsset.account_id == account.id).count(),
)
for account in accounts
]
click.echo(tabulate(account_data, headers=["Id", "Name", "Assets"]))


@fm_show_data.command("roles")
@with_appcontext
def list_roles():
"""
Show available account an user roles
"""
account_roles = AccountRole.query.order_by(AccountRole.name).all()
if not account_roles:
click.echo("No account roles created yet.")
return
click.echo("Account roles:\n")
click.echo(
tabulate(
[(r.id, r.name, r.description) for r in account_roles],
headers=["Id", "Name", "Description"],
)
)
click.echo()
user_roles = Role.query.order_by(Role.name).all()
if not user_roles:
click.echo("No user roles created yet, not even admin.")
return
click.echo("User roles:\n")
click.echo(
tabulate(
[(r.id, r.name, r.description) for r in user_roles],
headers=["Id", "Name", "Description"],
)
)


@fm_show_data.command("account")
@with_appcontext
@click.option("--account-id", type=int, required=True)
def show_account(account_id):
"""
Show information about an account, including users and assets.
"""
account: Account = Account.query.get(account_id)
if not account:
click.echo(f"No account with id {account_id} known.")
raise click.Abort

click.echo(f"========{len(account.name) * '='}==========")
click.echo(f"Account {account.name} (ID:{account.id}):")
click.echo(f"========{len(account.name) * '='}==========\n")

if account.account_roles:
click.echo(
f"Account role(s): {','.join([role.name for role in account.account_roles])}"
)
else:
click.echo("Account has no roles.")
click.echo()

users = User.query.filter_by(account_id=account_id).order_by(User.username).all()
if not users:
click.echo("No users in account ...")
else:
click.echo("All users:\n ")
user_data = [
(
user.id,
user.username,
user.email,
user.last_login_at,
"".join([role.name for role in user.roles]),
)
for user in users
]
click.echo(
tabulate(user_data, headers=["Id", "Name", "Email", "Last Login", "Roles"])
)

click.echo()
assets = (
GenericAsset.query.filter_by(account_id=account_id)
.order_by(GenericAsset.name)
.all()
)
if not assets:
click.echo("No assets in account ...")
else:
click.echo("All assets:\n ")
asset_data = [
(asset.id, asset.name, asset.generic_asset_type.name, asset.location)
for asset in assets
]
click.echo(tabulate(asset_data, headers=["Id", "Name", "Type", "Location"]))


@fm_show_data.command("asset-types")
@with_appcontext
def list_asset_types():
"""
Show available asset types
"""
asset_types = GenericAssetType.query.order_by(GenericAssetType.name).all()
if not asset_types:
click.echo("No asset types created yet.")
return
click.echo(
tabulate(
[(t.id, t.name, t.description) for t in asset_types],
headers=["Id", "Name", "Description"],
)
)


@fm_show_data.command("asset")
@with_appcontext
@click.option("--asset-id", type=int, required=True)
def show_generic_asset(asset_id):
"""
Show asset info and list sensors
"""
asset = GenericAsset.query.get(asset_id)
if not asset:
click.echo(f"No asset with id {asset_id} known.")
raise click.Abort

click.echo(f"======{len(asset.name) * '='}==========")
click.echo(f"Asset {asset.name} (ID:{asset.id}):")
click.echo(f"======{len(asset.name) * '='}==========\n")

asset_data = [
(
asset.generic_asset_type.name,
asset.location,
"".join([f"{k}:{v}\n" for k, v in asset.attributes.items()]),
)
]
click.echo(tabulate(asset_data, headers=["Type", "Location", "Attributes"]))

click.echo()
sensors = (
Sensor.query.filter_by(generic_asset_id=asset_id).order_by(Sensor.name).all()
)
if not sensors:
click.echo("No sensors in asset ...")
return
click.echo("All sensors in asset:\n ")
sensor_data = [
(
sensor.id,
sensor.name,
sensor.unit,
sensor.event_resolution,
sensor.timezone,
"".join([f"{k}:{v}\n" for k, v in sensor.attributes.items()]),
)
for sensor in sensors
]
click.echo(
tabulate(
sensor_data,
headers=["Id", "Name", "Unit", "Resolution", "Timezone", "Attributes"],
)
)


@fm_show_data.command("data-sources")
@with_appcontext
def list_data_sources():
"""
Show available data sources
"""
sources = DataSource.query.order_by(DataSource.name).all()
if not sources:
click.echo("No data sources created yet.")
return
click.echo(
tabulate(
[(s.id, s.name, s.type, s.user_id, s.model, s.version) for s in sources],
headers=["Id", "Name", "Type", "User Id", "Model", "Version"],
)
)


app.cli.add_command(fm_show_data)
1 change: 1 addition & 0 deletions requirements/app.in
Expand Up @@ -33,6 +33,7 @@ pvlib
netCDF4
siphon
tables
tabulate
timetomodel>=0.7.1
timely-beliefs>=1.11.0
python-dotenv
Expand Down
2 changes: 2 additions & 0 deletions requirements/app.txt
Expand Up @@ -361,6 +361,8 @@ statsmodels==0.13.0
# via timetomodel
tables==3.6.1
# via -r requirements/app.in
tabulate==0.8.9
# via -r requirements/app.in
threadpoolctl==3.0.0
# via scikit-learn
timely-beliefs==1.11.0
Expand Down

0 comments on commit 43a0173

Please sign in to comment.