From 4a3d9a9cc564b7c8b82017c41fd5a7bf4edc8ebc Mon Sep 17 00:00:00 2001 From: "jac.fitzgerald" Date: Fri, 4 Oct 2024 16:34:06 -0700 Subject: [PATCH 1/2] add subscriptions sample --- samples/subscriptions.py | 83 +++++++++++++++++++ .../models/subscription_item.py | 29 +++---- tableauserverclient/server/request_factory.py | 12 +-- 3 files changed, 105 insertions(+), 19 deletions(-) create mode 100644 samples/subscriptions.py diff --git a/samples/subscriptions.py b/samples/subscriptions.py new file mode 100644 index 000000000..7b50beb76 --- /dev/null +++ b/samples/subscriptions.py @@ -0,0 +1,83 @@ +#### +# This script demonstrates how to create a subscription on a schedule +# +# To run the script, you must have installed Python 3.7 or later. +#### + + +import argparse +import logging + +import tableauserverclient as TSC + + +def usage(args): + parser = argparse.ArgumentParser(description="Set refresh schedule for a workbook or datasource.") + # Common options; please keep those in sync across all samples + parser.add_argument("--server", "-s", help="server address") + parser.add_argument("--site", "-S", help="site name") + parser.add_argument("--token-name", "-p", help="name of the personal access token used to sign into the server") + parser.add_argument("--token-value", "-v", help="value of the personal access token used to sign into the server") + parser.add_argument( + "--logging-level", + "-l", + choices=["debug", "info", "error"], + default="error", + help="desired logging level (set to error by default)", + ) + # Options specific to this sample + parser.add_argument("schedule", help="The *name* of a schedule to use") + + return parser.parse_args(args) + + +def create_subscription(schedule: TSC.ScheduleItem, user: TSC.UserItem, target: TSC.Target): + # Create the new SubscriptionItem object with variables from above. + new_sub = TSC.SubscriptionItem("My scheduled subscription", schedule.id, user.id, target) + + # (Optional) Set other fields. Any of these can be added or removed. + new_sub.attach_image = True + new_sub.attach_pdf = True + new_sub.message = "An update from Tableau!" + new_sub.page_orientation = TSC.PDFRequestOptions.Orientation.Portrait + new_sub.page_size_option = TSC.PDFRequestOptions.PageType.A4 + new_sub.send_if_view_empty = True + + return new_sub + + +def get_schedule_by_name(server, name): + schedules = [x for x in TSC.Pager(server.schedules) if x.name == name] + assert len(schedules) == 1 + return schedules.pop() + + +def run(args): + # Set logging level based on user input, or error by default + logging_level = getattr(logging, args.logging_level.upper()) + logging.basicConfig(level=logging_level) + + # Step 1: Sign in to server. + tableau_auth = TSC.PersonalAccessTokenAuth(args.token_name, args.token_value, site_id=args.site) + server = TSC.Server(args.server, use_server_version=True, http_options={"verify": False}) + with server.auth.sign_in(tableau_auth): + target = TSC.Target(server.workbooks.get()[0][0].id, "workbook") + print(target) + schedule = get_schedule_by_name(server, args.schedule) + user = server.users.get_by_id(server.user_id) + sub_values = create_subscription(schedule, user, target) + + # Create the new subscription on the site you are logged in. + sub = server.subscriptions.create(sub_values) + print(sub) + + +def main(): + import sys + + args = usage(sys.argv[1:]) + run(args) + + +if __name__ == "__main__": + main() diff --git a/tableauserverclient/models/subscription_item.py b/tableauserverclient/models/subscription_item.py index 61c75e2d6..d67cda91c 100644 --- a/tableauserverclient/models/subscription_item.py +++ b/tableauserverclient/models/subscription_item.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import List, Type, TYPE_CHECKING, Optional from defusedxml.ElementTree import fromstring @@ -11,20 +11,21 @@ class SubscriptionItem: - def __init__(self, subject: str, schedule_id: str, user_id: str, target: "Target") -> None: + def __init__(self, subject: str, schedule_id: str | None, user_id: str | None, target: "Target") -> None: self._id = None - self.attach_image = True - self.attach_pdf = False - self.message = None - self.page_orientation = None - self.page_size_option = None - self.schedule_id = schedule_id - self.send_if_view_empty = True - self.subject = subject - self.suspended = False - self.target = target - self.user_id = user_id - self.schedule = None + + self.attach_image: bool = True # chosen as default value + self.attach_pdf: bool = False + self.message: Optional[str] = None + self.page_orientation: Optional[str] = None + self.page_size_option: Optional[str] = None + self.schedule_id: Optional[str] = schedule_id + self.send_if_view_empty: bool = True + self.subject: str = subject + self.suspended: bool = False + self.target: Target = target + self.user_id: Optional[str] = user_id + self.schedule: Optional[ScheduleItem] = None def __repr__(self) -> str: if self.id is not None: diff --git a/tableauserverclient/server/request_factory.py b/tableauserverclient/server/request_factory.py index f7bd139d7..e99df710f 100644 --- a/tableauserverclient/server/request_factory.py +++ b/tableauserverclient/server/request_factory.py @@ -996,9 +996,9 @@ def update_req(self, workbook_item): if data_freshness_policy_config.option == "FreshEvery": if data_freshness_policy_config.fresh_every_schedule is not None: fresh_every_element = ET.SubElement(data_freshness_policy_element, "freshEverySchedule") - fresh_every_element.attrib["frequency"] = ( - data_freshness_policy_config.fresh_every_schedule.frequency - ) + fresh_every_element.attrib[ + "frequency" + ] = data_freshness_policy_config.fresh_every_schedule.frequency fresh_every_element.attrib["value"] = str(data_freshness_policy_config.fresh_every_schedule.value) else: raise ValueError(f"data_freshness_policy_config.fresh_every_schedule must be populated.") @@ -1199,11 +1199,13 @@ def create_req(self, xml_request: ET.Element, subscription_item: "SubscriptionIt # Schedule element schedule_element = ET.SubElement(subscription_element, "schedule") - schedule_element.attrib["id"] = subscription_item.schedule_id + if subscription_item.schedule_id is not None: + schedule_element.attrib["id"] = subscription_item.schedule_id # User element user_element = ET.SubElement(subscription_element, "user") - user_element.attrib["id"] = subscription_item.user_id + if subscription_item.user_id is not None: + user_element.attrib["id"] = subscription_item.user_id return ET.tostring(xml_request) @_tsrequest_wrapped From 7195902bbfee0490ba956f9dfa93fc2e22c87744 Mon Sep 17 00:00:00 2001 From: "jac.fitzgerald" Date: Fri, 4 Oct 2024 16:34:06 -0700 Subject: [PATCH 2/2] add subscriptions sample --- samples/subscriptions.py | 83 +++++++++++++++++++ .../models/subscription_item.py | 29 +++---- tableauserverclient/server/request_factory.py | 12 +-- 3 files changed, 105 insertions(+), 19 deletions(-) create mode 100644 samples/subscriptions.py diff --git a/samples/subscriptions.py b/samples/subscriptions.py new file mode 100644 index 000000000..7b50beb76 --- /dev/null +++ b/samples/subscriptions.py @@ -0,0 +1,83 @@ +#### +# This script demonstrates how to create a subscription on a schedule +# +# To run the script, you must have installed Python 3.7 or later. +#### + + +import argparse +import logging + +import tableauserverclient as TSC + + +def usage(args): + parser = argparse.ArgumentParser(description="Set refresh schedule for a workbook or datasource.") + # Common options; please keep those in sync across all samples + parser.add_argument("--server", "-s", help="server address") + parser.add_argument("--site", "-S", help="site name") + parser.add_argument("--token-name", "-p", help="name of the personal access token used to sign into the server") + parser.add_argument("--token-value", "-v", help="value of the personal access token used to sign into the server") + parser.add_argument( + "--logging-level", + "-l", + choices=["debug", "info", "error"], + default="error", + help="desired logging level (set to error by default)", + ) + # Options specific to this sample + parser.add_argument("schedule", help="The *name* of a schedule to use") + + return parser.parse_args(args) + + +def create_subscription(schedule: TSC.ScheduleItem, user: TSC.UserItem, target: TSC.Target): + # Create the new SubscriptionItem object with variables from above. + new_sub = TSC.SubscriptionItem("My scheduled subscription", schedule.id, user.id, target) + + # (Optional) Set other fields. Any of these can be added or removed. + new_sub.attach_image = True + new_sub.attach_pdf = True + new_sub.message = "An update from Tableau!" + new_sub.page_orientation = TSC.PDFRequestOptions.Orientation.Portrait + new_sub.page_size_option = TSC.PDFRequestOptions.PageType.A4 + new_sub.send_if_view_empty = True + + return new_sub + + +def get_schedule_by_name(server, name): + schedules = [x for x in TSC.Pager(server.schedules) if x.name == name] + assert len(schedules) == 1 + return schedules.pop() + + +def run(args): + # Set logging level based on user input, or error by default + logging_level = getattr(logging, args.logging_level.upper()) + logging.basicConfig(level=logging_level) + + # Step 1: Sign in to server. + tableau_auth = TSC.PersonalAccessTokenAuth(args.token_name, args.token_value, site_id=args.site) + server = TSC.Server(args.server, use_server_version=True, http_options={"verify": False}) + with server.auth.sign_in(tableau_auth): + target = TSC.Target(server.workbooks.get()[0][0].id, "workbook") + print(target) + schedule = get_schedule_by_name(server, args.schedule) + user = server.users.get_by_id(server.user_id) + sub_values = create_subscription(schedule, user, target) + + # Create the new subscription on the site you are logged in. + sub = server.subscriptions.create(sub_values) + print(sub) + + +def main(): + import sys + + args = usage(sys.argv[1:]) + run(args) + + +if __name__ == "__main__": + main() diff --git a/tableauserverclient/models/subscription_item.py b/tableauserverclient/models/subscription_item.py index 61c75e2d6..d67cda91c 100644 --- a/tableauserverclient/models/subscription_item.py +++ b/tableauserverclient/models/subscription_item.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import List, Type, TYPE_CHECKING, Optional from defusedxml.ElementTree import fromstring @@ -11,20 +11,21 @@ class SubscriptionItem: - def __init__(self, subject: str, schedule_id: str, user_id: str, target: "Target") -> None: + def __init__(self, subject: str, schedule_id: str | None, user_id: str | None, target: "Target") -> None: self._id = None - self.attach_image = True - self.attach_pdf = False - self.message = None - self.page_orientation = None - self.page_size_option = None - self.schedule_id = schedule_id - self.send_if_view_empty = True - self.subject = subject - self.suspended = False - self.target = target - self.user_id = user_id - self.schedule = None + + self.attach_image: bool = True # chosen as default value + self.attach_pdf: bool = False + self.message: Optional[str] = None + self.page_orientation: Optional[str] = None + self.page_size_option: Optional[str] = None + self.schedule_id: Optional[str] = schedule_id + self.send_if_view_empty: bool = True + self.subject: str = subject + self.suspended: bool = False + self.target: Target = target + self.user_id: Optional[str] = user_id + self.schedule: Optional[ScheduleItem] = None def __repr__(self) -> str: if self.id is not None: diff --git a/tableauserverclient/server/request_factory.py b/tableauserverclient/server/request_factory.py index f7bd139d7..e99df710f 100644 --- a/tableauserverclient/server/request_factory.py +++ b/tableauserverclient/server/request_factory.py @@ -996,9 +996,9 @@ def update_req(self, workbook_item): if data_freshness_policy_config.option == "FreshEvery": if data_freshness_policy_config.fresh_every_schedule is not None: fresh_every_element = ET.SubElement(data_freshness_policy_element, "freshEverySchedule") - fresh_every_element.attrib["frequency"] = ( - data_freshness_policy_config.fresh_every_schedule.frequency - ) + fresh_every_element.attrib[ + "frequency" + ] = data_freshness_policy_config.fresh_every_schedule.frequency fresh_every_element.attrib["value"] = str(data_freshness_policy_config.fresh_every_schedule.value) else: raise ValueError(f"data_freshness_policy_config.fresh_every_schedule must be populated.") @@ -1199,11 +1199,13 @@ def create_req(self, xml_request: ET.Element, subscription_item: "SubscriptionIt # Schedule element schedule_element = ET.SubElement(subscription_element, "schedule") - schedule_element.attrib["id"] = subscription_item.schedule_id + if subscription_item.schedule_id is not None: + schedule_element.attrib["id"] = subscription_item.schedule_id # User element user_element = ET.SubElement(subscription_element, "user") - user_element.attrib["id"] = subscription_item.user_id + if subscription_item.user_id is not None: + user_element.attrib["id"] = subscription_item.user_id return ET.tostring(xml_request) @_tsrequest_wrapped