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

Make flexmeasures add schedule a subgroup #557

Merged
merged 2 commits into from Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
118 changes: 19 additions & 99 deletions flexmeasures/cli/data_add.py
Expand Up @@ -18,6 +18,7 @@
import timely_beliefs.utils as tb_utils
from workalendar.registry import registry as workalendar_registry

from flexmeasures.cli.utils import DeprecatedDefaultGroup
from flexmeasures.data import db
from flexmeasures.data.scripts.data_gen import (
add_transmission_zone_asset,
Expand Down Expand Up @@ -821,111 +822,30 @@ def create_forecasts(
)


# TODO: deprecate in v0.13
@fm_add_data.command("schedule")
# todo: repurpose `flexmeasures add schedule` (deprecated since v0.12),
# - see https://github.com/FlexMeasures/flexmeasures/pull/537#discussion_r1048680231
# - hint for repurposing to invoke custom logic instead of a default subcommand:
# @fm_add_data.group("schedule", invoke_without_command=True)
# def create_schedule():
# if ctx.invoked_subcommand:
# ...
@fm_add_data.group(
"schedule",
cls=DeprecatedDefaultGroup,
default="storage",
deprecation_message="The command 'flexmeasures add schedule' is deprecated. Please use `flexmeasures add schedule storage` instead.",
)
@click.pass_context
@with_appcontext
@click.option(
"--sensor-id",
"power_sensor",
type=SensorIdField(),
required=True,
help="Create schedule for this sensor. Follow up with the sensor's ID.",
)
@click.option(
"--consumption-price-sensor",
"consumption_price_sensor",
type=SensorIdField(),
required=False,
help="Optimize consumption against this sensor. The sensor typically records an electricity price (e.g. in EUR/kWh), but this field can also be used to optimize against some emission intensity factor (e.g. in kg CO₂ eq./kWh). Follow up with the sensor's ID.",
)
@click.option(
"--production-price-sensor",
"production_price_sensor",
type=SensorIdField(),
required=False,
help="Optimize production against this sensor. Defaults to the consumption price sensor. The sensor typically records an electricity price (e.g. in EUR/kWh), but this field can also be used to optimize against some emission intensity factor (e.g. in kg CO₂ eq./kWh). Follow up with the sensor's ID.",
)
@click.option(
"--optimization-context-id",
"optimization_context_sensor",
type=SensorIdField(),
required=False,
help="To be deprecated. Use consumption-price-sensor instead.",
)
@click.option(
"--start",
"start",
type=AwareDateTimeField(format="iso"),
required=True,
help="Schedule starts at this datetime. Follow up with a timezone-aware datetime in ISO 6801 format.",
)
@click.option(
"--duration",
"duration",
type=DurationField(),
required=True,
help="Duration of schedule, after --start. Follow up with a duration in ISO 6801 format, e.g. PT1H (1 hour) or PT45M (45 minutes).",
)
@click.option(
"--soc-at-start",
"soc_at_start",
type=QuantityField("%", validate=validate.Range(min=0, max=1)),
required=True,
help="State of charge (e.g 32.8%, or 0.328) at the start of the schedule.",
)
@click.option(
"--soc-target",
"soc_target_strings",
type=click.Tuple(
types=[QuantityField("%", validate=validate.Range(min=0, max=1)), str]
),
multiple=True,
required=False,
help="Target state of charge (e.g 100%, or 1) at some datetime. Follow up with a float value and a timezone-aware datetime in ISO 6081 format."
" This argument can be given multiple times."
" For example: --soc-target 100% 2022-02-23T13:40:52+00:00",
)
@click.option(
"--soc-min",
"soc_min",
type=QuantityField("%", validate=validate.Range(min=0, max=1)),
required=False,
help="Minimum state of charge (e.g 20%, or 0.2) for the schedule.",
)
@click.option(
"--soc-max",
"soc_max",
type=QuantityField("%", validate=validate.Range(min=0, max=1)),
required=False,
help="Maximum state of charge (e.g 80%, or 0.8) for the schedule.",
)
@click.option(
"--roundtrip-efficiency",
"roundtrip_efficiency",
type=QuantityField("%", validate=validate.Range(min=0, max=1)),
required=False,
default=1,
help="Round-trip efficiency (e.g. 85% or 0.85) to use for the schedule. Defaults to 100% (no losses).",
)
@click.option(
"--as-job",
is_flag=True,
help="Whether to queue a scheduling job instead of computing directly. "
"To process the job, run a worker (on any computer, but configured to the same databases) to process the 'scheduling' queue. Defaults to False.",
)
def create_schedule(ctx, **kwargs):
"""[deprecated] Create a new schedule for a given power sensor.
def create_schedule(ctx):
"""(Deprecated) Create a new schedule for a given power sensor.

THIS COMMAND HAS BEEN RENAMED, please use `flexmeasures add storage-schedule`
THIS COMMAND HAS BEEN RENAMED TO `flexmeasures add schedule storage`
"""
click.echo(
"THIS COMMAND HAS BEEN RENAMED TO flexmeasures add schedule-for-storage. IT WILL BE DEPRECATED IN v0.13."
)
ctx.invoke(add_schedule_for_storage, **kwargs)
pass


@fm_add_data.command("schedule-for-storage")
@create_schedule.command("storage")
nhoening marked this conversation as resolved.
Show resolved Hide resolved
@with_appcontext
@click.option(
"--sensor-id",
Expand Down
48 changes: 48 additions & 0 deletions flexmeasures/cli/utils.py
@@ -0,0 +1,48 @@
import click
from click_default_group import DefaultGroup


class DeprecatedDefaultGroup(DefaultGroup):
"""Invokes a default subcommand, *and* shows a deprecation message.

Also adds the `invoked_default` boolean attribute to the context.
A group callback can use this information to figure out if it's being executed directly
(invoking the default subcommand) or because the execution flow passes onwards to a subcommand.
By default it's None, but it can be the name of the default subcommand to execute.

.. sourcecode:: python

import click
from flexmeasures.cli.utils import DeprecatedDefaultGroup

@click.group(cls=DeprecatedDefaultGroup, default="bar", deprecation_message="renamed to `foo bar`.")
def foo(ctx):
if ctx.invoked_default:
click.echo("foo")

@foo.command()
def bar():
click.echo("bar")

.. sourcecode:: console

$ flexmeasures foo
DeprecationWarning: renamed to `foo bar`.
foo
bar
$ flexmeasures foo bar
bar
"""

def __init__(self, *args, **kwargs):
self.deprecation_message = "DeprecationWarning: " + kwargs.pop(
"deprecation_message", ""
)
super().__init__(*args, **kwargs)

def get_command(self, ctx, cmd_name):
ctx.invoked_default = None
if cmd_name not in self.commands:
click.echo(click.style(self.deprecation_message, fg="red"), err=True)
ctx.invoked_default = self.default_cmd_name
return super().get_command(ctx, cmd_name)
1 change: 1 addition & 0 deletions requirements/app.in
Expand Up @@ -17,6 +17,7 @@ pytz
numpy
isodate
click
click-default-group
email_validator
rq
rq-dashboard
Expand Down
3 changes: 3 additions & 0 deletions requirements/app.txt
Expand Up @@ -31,8 +31,11 @@ charset-normalizer==2.1.1
click==8.1.3
# via
# -r requirements/app.in
# click-default-group
# flask
# rq
click-default-group==1.2.2
# via -r requirements/app.in
colour==0.1.5
# via -r requirements/app.in
convertdate==2.4.0
Expand Down