Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add flexmeasures show CLI commands #339

Merged
merged 6 commits into from Jan 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions documentation/changelog.rst
Expand Up @@ -8,6 +8,8 @@ v0.9.0 | February XX, 2022
New features
-----------

* 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.6.0 | April 2, 2021
=====================
Expand Down
13 changes: 13 additions & 0 deletions documentation/cli/commands.rst
Expand Up @@ -35,6 +35,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.10.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.10.0
Expand Down