Repository: planetlabs/planet-client-python

Issue:
Add a "planet subscriptions request-pv" command
Like `planet subscriptions catalog-request` https://planet-sdk-for-python-v2.readthedocs.io/en/latest/cli/cli-subscriptions/#catalog-request

Discussion:

Patch:
diff --git a/CHANGES.txt b/CHANGES.txt
index 9163bfe..18b7033 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -3,6 +3,9 @@
 2.1b1 (2023-07-11)
 
 Added:
+- The request command of the subscriptions CLI has a new --clip-to-source
+  option (#988).
+- A new request-pv command has been added to the subscriptions CLi (#988).
 - Support for catalog source publishing stages (#977) and time range types
   (#978) have been added to subscription_request.catalog_source.
 - Add the option to get Planetary Variable subscription results as a CSV file
diff --git a/planet/cli/subscriptions.py b/planet/cli/subscriptions.py
index 62860c0..f5551e8 100644
--- a/planet/cli/subscriptions.py
+++ b/planet/cli/subscriptions.py
@@ -238,14 +238,32 @@ async def list_subscription_results_cmd(ctx,
     '--tools',
     type=types.JSON(),
     help='Toolchain JSON. Can be a string, filename, or - for stdin.')
+@click.option(
+    '--clip-to-source',
+    is_flag=True,
+    default=False,
+    help="Clip to the source geometry without specifying a clip tool.")
 @pretty
-def request(name, source, delivery, notifications, tools, pretty):
-    """Generate a subscriptions request."""
+def request(name,
+            source,
+            delivery,
+            notifications,
+            tools,
+            clip_to_source,
+            pretty):
+    """Generate a subscriptions request.
+
+    Note: the next version of the Subscription API will remove the clip
+    tool option and always clip to the source geometry. Thus the
+    --clip-to-source option is a preview of the next API version's
+    default behavior.
+    """
     res = subscription_request.build_request(name,
                                              source,
                                              delivery,
                                              notifications=notifications,
-                                             tools=tools)
+                                             tools=tools,
+                                             clip_to_source=clip_to_source)
     echo_json(res, pretty)
 
 
@@ -298,3 +316,49 @@ def request_catalog(item_types,
                                               rrule=rrule,
                                               filter=filter)
     echo_json(res, pretty)
+
+
+@subscriptions.command()  # type: ignore
+@translate_exceptions
+@click.option(
+    '--var-type',
+    required=True,
+    help='Planetary variable type.',
+    type=click.Choice([
+        "biomass_proxy",
+        "land_surface_temperature",
+        "soil_water_content",
+        "vegetation_optical_depth"
+    ]),
+)
+@click.option('--var-id', required=True, help='Planetary variable id.')
+@click.option(
+    '--geometry',
+    required=True,
+    type=types.JSON(),
+    help="""Geometry of the area of interest of the subscription that will be
+    used to determine matches. Can be a string, filename, or - for stdin.""")
+@click.option('--start-time',
+              required=True,
+              type=types.DateTime(),
+              help='Date and time to begin subscription.')
+@click.option('--end-time',
+              type=types.DateTime(),
+              help='Date and time to end subscription.')
+@pretty
+def request_pv(var_type, var_id, geometry, start_time, end_time, pretty):
+    """Generate a Planetary Variable subscription source.
+
+    Planetary Variables come in 4 types and are further subdivided
+    within these types. See [Subscribing to Planetary
+    Variables](https://developers.planet.com/docs/subscriptions/pvs-subs/#planetary-variables-types-and-ids)
+    for details.
+    """
+    res = subscription_request.planetary_variable_source(
+        var_type,
+        var_id,
+        geometry,
+        start_time,
+        end_time=end_time,
+    )
+    echo_json(res, pretty)
diff --git a/planet/subscription_request.py b/planet/subscription_request.py
index 2118a09..ca6bff2 100644
--- a/planet/subscription_request.py
+++ b/planet/subscription_request.py
@@ -118,8 +118,8 @@ def build_request(name: str,
     if notifications:
         details['notifications'] = dict(notifications)
 
-    if tools:
-        tool_list = [dict(tool) for tool in tools]
+    if tools or clip_to_source:
+        tool_list = [dict(tool) for tool in (tools or [])]
 
         # If clip_to_source is True a clip configuration will be added
         # to the list of requested tools unless an existing clip tool


Test patch:
diff --git a/tests/integration/test_subscriptions_cli.py b/tests/integration/test_subscriptions_cli.py
index 5fe4b42..a1b3cea 100644
--- a/tests/integration/test_subscriptions_cli.py
+++ b/tests/integration/test_subscriptions_cli.py
@@ -252,7 +252,7 @@ def test_subscriptions_results_success(invoke, options, expected_count):
 
 
 def test_request_base_success(invoke, geom_geojson):
-    """Request command succeeds"""
+    """Request command succeeds."""
     source = json.dumps({
         "type": "catalog",
         "parameters": {
@@ -285,6 +285,32 @@ def test_request_base_success(invoke, geom_geojson):
     assert result.exit_code == 0  # success.
 
 
+def test_request_base_clip_to_source(invoke, geom_geojson):
+    """Clip to source using command line option."""
+    source = json.dumps({
+        "type": "catalog",
+        "parameters": {
+            "geometry": geom_geojson,
+            "start_time": "2021-03-01T00:00:00Z",
+            "item_types": ["PSScene"],
+            "asset_types": ["ortho_analytic_4b"]
+        }
+    })
+    result = invoke([
+        'request',
+        '--name=test',
+        f'--source={source}',
+        '--delivery={"type": "fake"}',
+        '--clip-to-source'
+    ])
+
+    assert result.exit_code == 0  # success.
+    req = json.loads(result.output)
+    tool = req["tools"][0]
+    assert tool["type"] == "clip"
+    assert tool["parameters"]["aoi"] == geom_geojson
+
+
 def test_request_catalog_success(invoke, geom_geojson):
     """Request-catalog command succeeds"""
     source = {
@@ -314,3 +340,19 @@ def test_subscriptions_results_csv(invoke):
     result = invoke(['results', 'test', '--csv'])
     assert result.exit_code == 0  # success.
     assert result.output.splitlines() == ['id,status', '1234-abcd,SUCCESS']
+
+
+def test_request_pv_success(invoke, geom_geojson):
+    """Request-pv command succeeds"""
+    result = invoke([
+        'request-pv',
+        '--var-type=biomass_proxy',
+        '--var-id=BIOMASS-PROXY_V3.0_10',
+        f"--geometry={json.dumps(geom_geojson)}",
+        '--start-time=2021-03-01T00:00:00'
+    ])
+
+    assert result.exit_code == 0  # success.
+    source = json.loads(result.output)
+    assert source["type"] == "biomass_proxy"
+    assert source["parameters"]["id"] == "BIOMASS-PROXY_V3.0_10"