Repository: planetlabs/planet-client-python

Issue:
Add publishing_stages to catalog source parameters
It's a list of choices from "preview", "standard", or "finalized". See https://developers.planet.com/docs/subscriptions/reference/#tag/subscriptions/operation/createSubscription.

Discussion:

Patch:
diff --git a/CHANGES.txt b/CHANGES.txt
index 2255bc0..105eff2 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,7 +1,10 @@
 2.1.0 (TBD)
 
 Added:
-- Add the option to get Planetary Variable subscription results as a CSV file ().
+- Support for catalog source publishing stages has been added to
+  subscription_request.catalog_source (#977).
+- Add the option to get Planetary Variable subscription results as a CSV file
+  (#981).
 - A subscription_request.planetary_variable_source function has been added
   (#976).
 - The subscription_request.build_request function has a new option to clip to
diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css
index 772894c..281041a 100644
--- a/docs/stylesheets/extra.css
+++ b/docs/stylesheets/extra.css
@@ -85,3 +85,6 @@ section.mdx-container::before{
     }
 }
 
+.highlight .gp, .highlight .go { /* Generic.Prompt, Generic.Output */
+    user-select: none;
+  }
\ No newline at end of file
diff --git a/planet/subscription_request.py b/planet/subscription_request.py
index f0e7d8d..4d494ec 100644
--- a/planet/subscription_request.py
+++ b/planet/subscription_request.py
@@ -13,7 +13,7 @@
 # the License.
 """Functionality for preparing subscription requests."""
 from datetime import datetime
-from typing import Any, Dict, Optional, List, Literal, Mapping
+from typing import Any, Dict, Optional, List, Literal, Mapping, Sequence
 
 from . import geojson, specs
 from .exceptions import ClientError
@@ -151,6 +151,9 @@ def catalog_source(
     filter: Optional[Mapping] = None,
     end_time: Optional[datetime] = None,
     rrule: Optional[str] = None,
+    publishing_stages: Optional[Sequence[Literal["preview",
+                                                 "standard",
+                                                 "finalized"]]] = None,
 ) -> dict:
     """Construct a Catalog subscription source.
 
@@ -173,6 +176,8 @@ def catalog_source(
             the past or future, and must be after the start_time.
         rrule: The recurrence rule, given in iCalendar RFC 5545 format.
             Only monthly recurrences are supported at this time.
+        publishing_stages: A sequence of one or more of the values
+            "preview", "standard", or "finalized".
 
     Returns:
         dict: a representation of a subscription source.
@@ -180,6 +185,29 @@ def catalog_source(
     Raises:
         ClientError: if a source can not be
             configured.
+
+    Examples:
+        ```pycon
+        >>> source = catalog_source(
+        ...     ["PSScene"],
+        ...     ["ortho_analytic_4b"],
+        ...     geometry={
+        ...         "type": "Polygon",
+        ...         "coordinates": [[[37.791595458984375, 14.84923123791421],
+        ...                         [37.90214538574219, 14.84923123791421],
+        ...                         [37.90214538574219, 14.945448293647944],
+        ...                         [37.791595458984375, 14.945448293647944],
+        ...                         [37.791595458984375, 14.84923123791421]]]
+        ...     },
+        ...     start_time=datetime(2021, 3, 1),
+        ...     publishing_stages=["standard"],
+        ... )
+        >>> request = build_request(
+        ...     "Standard PSScene Ortho Analytic",
+        ...     source=source,
+        ...     delivery={})
+        ```
+
     """
     if len(item_types) > 1:
         raise ClientError(
@@ -216,6 +244,9 @@ def catalog_source(
     if rrule:
         parameters['rrule'] = rrule
 
+    if publishing_stages:
+        parameters['publishing_stages'] = list(set(publishing_stages))
+
     return {"type": "catalog", "parameters": parameters}
 
 
@@ -275,7 +306,10 @@ def planetary_variable_source(
         ...     },
         ...     start_time=datetime(2021, 3, 1)
         ... )
-        >>> request = build_request(source=source, ...)
+        >>> request = build_request(
+        ...     "Soil Water Content",
+        ...     source=source,
+        ...     delivery={})
         ```
     """
     # TODO: validation of variable types and ids.


Test patch:
diff --git a/tests/unit/test_subscription_request.py b/tests/unit/test_subscription_request.py
index d24dbd3..34cd5fa 100644
--- a/tests/unit/test_subscription_request.py
+++ b/tests/unit/test_subscription_request.py
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 from datetime import datetime
+import itertools
 import logging
 
 import pytest
@@ -369,3 +370,27 @@ def test_pv_source_success(geom_geojson, var_type, var_id):
     assert params["id"] == var_id
     assert params["geometry"] == geom_geojson
     assert params["start_time"].startswith("2021-03-01")
+
+
+@pytest.mark.parametrize(
+    # Test all the combinations of the three options plus some with dupes.
+    "publishing_stages",
+    list(
+        itertools.chain.from_iterable(
+            itertools.combinations(["preview", "standard", "finalized"], i)
+            for i in range(1, 4))) + [("preview", "preview"),
+                                      ("preview", "finalized", "preview")])
+def test_catalog_source_publishing_stages(publishing_stages, geom_geojson):
+    """Configure publishing stages for a catalog source."""
+    source = subscription_request.catalog_source(
+        item_types=["PSScene"],
+        asset_types=["ortho_analytic_4b"],
+        geometry=geom_geojson,
+        start_time=datetime(2021, 3, 1),
+        end_time=datetime(2023, 11, 1),
+        rrule="FREQ=MONTHLY;BYMONTH=3,4,5,6,7,8,9,10",
+        publishing_stages=publishing_stages,
+    )
+
+    assert source["parameters"]["publishing_stages"] == list(
+        set(publishing_stages))