diff --git a/tableauserverclient/__init__.py b/tableauserverclient/__init__.py index 9e56919c6..af69ee128 100644 --- a/tableauserverclient/__init__.py +++ b/tableauserverclient/__init__.py @@ -1,5 +1,5 @@ from .namespace import NAMESPACE -from .models import ConnectionItem, DatasourceItem,\ +from .models import ConnectionItem, DatasourceItem, ExtractRefreshTaskItem,\ GroupItem, PaginationItem, ProjectItem, ScheduleItem, \ SiteItem, TableauAuth, UserItem, ViewItem, WorkbookItem, UnpopulatedPropertyError, \ HourlyInterval, DailyInterval, WeeklyInterval, MonthlyInterval, IntervalItem diff --git a/tableauserverclient/models/__init__.py b/tableauserverclient/models/__init__.py index 276684d66..596d3464b 100644 --- a/tableauserverclient/models/__init__.py +++ b/tableauserverclient/models/__init__.py @@ -1,6 +1,7 @@ from .connection_item import ConnectionItem from .datasource_item import DatasourceItem from .exceptions import UnpopulatedPropertyError +from .extract_refresh_task_item import ExtractRefreshTaskItem from .group_item import GroupItem from .interval_item import IntervalItem, DailyInterval, WeeklyInterval, MonthlyInterval, HourlyInterval from .pagination_item import PaginationItem diff --git a/tableauserverclient/models/exceptions.py b/tableauserverclient/models/exceptions.py index 28d738e73..fc5ddd71f 100644 --- a/tableauserverclient/models/exceptions.py +++ b/tableauserverclient/models/exceptions.py @@ -1,2 +1,5 @@ class UnpopulatedPropertyError(Exception): pass + +class ResponseBodyError(Exception): + pass \ No newline at end of file diff --git a/tableauserverclient/models/extract_refresh_task_item.py b/tableauserverclient/models/extract_refresh_task_item.py new file mode 100644 index 000000000..ae8dc1ac1 --- /dev/null +++ b/tableauserverclient/models/extract_refresh_task_item.py @@ -0,0 +1,93 @@ +import xml.etree.ElementTree as ET +from .. import NAMESPACE +from item_types import ItemTypes +from task_item import TaskItem +from exceptions import ResponseBodyError +from property_decorators import property_is_int, property_is_enum + +class ExtractRefreshTaskItem(TaskItem): + + class RefreshType: + FullRefresh = "FullRefresh" + IncrementalRefresh = "IncrementalRefresh" + + # A user should never create this item. It should only ever be created by a return call from + # a REST API. Possibly we could be more vigilant in hiding the constructor + def __init__(self, id, schedule_id, priority, refresh_type, item_type, item_id): + super(ExtractRefreshTaskItem, self).__init__(id, schedule_id) + self.priority = priority + self.type = type + self.refresh_type = refresh_type + self.item_type = item_type + self.item_id = item_id + + @property + def priority(self): + return self._priority + + @priority.setter + @property_is_int(range=(1, 100)) + def priority(self, value): + self._priority = value + + @property + def refresh_type(self): + return self._refresh_type + + @refresh_type.setter + @property_is_enum(RefreshType) + def refresh_type(self, value): + self._refresh_type = value + + @property + def item_type(self): + return self._item_type + + @item_type.setter + def item_type(self, value): + # Check that it is Datasource or Workbook + if not (value in [ItemTypes.Datasource, ItemTypes.Workbook]): + error = "Invalid value: {0}. item_type must be either ItemTypes.Datasource or ItemTypes.Workbook".format(value) + raise ValueError(error) + self._item_type = value + + @property + def item_id(self): + return self._item_id + + @item_id.setter + def item_id(self, value): + self._item_id = value + + + @classmethod + def from_response(cls, resp, schedule_id): + tasks_items = list() + parsed_response = ET.fromstring(resp) + extract_tags = parsed_response.findall('.//t:extract', namespaces=NAMESPACE) + for extract_tag in extract_tags: + (id, priority, refresh_type, item_type, item_id) = cls._parse_element(extract_tag) + + task = cls(id, schedule_id, priority, refresh_type, item_type, item_id) + tasks_items.append(task) + return tasks_items + + @staticmethod + def _parse_element(extract_tag): + id = extract_tag.get('id') + priority = int(extract_tag.get('priority')) + refresh_type = extract_tag.get('type') + + datasource_tag = extract_tag.find('.//t:datasource', namespaces=NAMESPACE) + workbook_tag = extract_tag.find('.//t:workbook', namespaces=NAMESPACE) + if datasource_tag is not None: + item_type = ItemTypes.Datasource + item_id = datasource_tag.get('id') + elif workbook_tag is not None: + item_type = ItemTypes.Workbook + item_id = workbook_tag.get('id') + else: + error = "Missing workbook / datasource element for refresh task" + raise ResponseBodyError(error) + + return id, priority, refresh_type, item_type, item_id \ No newline at end of file diff --git a/tableauserverclient/models/item_types.py b/tableauserverclient/models/item_types.py new file mode 100644 index 000000000..9a4562f3d --- /dev/null +++ b/tableauserverclient/models/item_types.py @@ -0,0 +1,3 @@ +class ItemTypes: + Datasource = 'Datasource' + Workbook = 'Workbook' \ No newline at end of file diff --git a/tableauserverclient/models/task_item.py b/tableauserverclient/models/task_item.py new file mode 100644 index 000000000..9e9b9294b --- /dev/null +++ b/tableauserverclient/models/task_item.py @@ -0,0 +1,13 @@ +class TaskItem(object): + + def __init__(self, id, schedule_id): + self._id = id + self._schedule_id = schedule_id + + @property + def id(self): + return self._id + + @property + def schedule_id(self): + return self._schedule_id \ No newline at end of file diff --git a/tableauserverclient/server/__init__.py b/tableauserverclient/server/__init__.py index a8b78b7fc..39910ad29 100644 --- a/tableauserverclient/server/__init__.py +++ b/tableauserverclient/server/__init__.py @@ -2,10 +2,10 @@ from .request_options import RequestOptions from .filter import Filter from .sort import Sort -from .. import ConnectionItem, DatasourceItem,\ +from .. import ConnectionItem, DatasourceItem, ExtractRefreshTaskItem,\ GroupItem, PaginationItem, ProjectItem, ScheduleItem, SiteItem, TableauAuth,\ UserItem, ViewItem, WorkbookItem, NAMESPACE from .endpoint import Auth, Datasources, Endpoint, Groups, Projects, Schedules, \ - Sites, Users, Views, Workbooks, ServerResponseError, MissingRequiredFieldError + Sites, Tasks, Users, Views, Workbooks, ServerResponseError, MissingRequiredFieldError from .server import Server from .exceptions import NotSignedInError diff --git a/tableauserverclient/server/endpoint/__init__.py b/tableauserverclient/server/endpoint/__init__.py index 65e15c683..e61ed6f88 100644 --- a/tableauserverclient/server/endpoint/__init__.py +++ b/tableauserverclient/server/endpoint/__init__.py @@ -6,6 +6,7 @@ from .projects_endpoint import Projects from .schedules_endpoint import Schedules from .sites_endpoint import Sites +from .tasks_endpoint import Tasks from .users_endpoint import Users from .views_endpoint import Views from .workbooks_endpoint import Workbooks diff --git a/tableauserverclient/server/endpoint/extract_refreshes_endpoint.py b/tableauserverclient/server/endpoint/extract_refreshes_endpoint.py new file mode 100644 index 000000000..383528b32 --- /dev/null +++ b/tableauserverclient/server/endpoint/extract_refreshes_endpoint.py @@ -0,0 +1,21 @@ +from .endpoint import Endpoint +from .. import PaginationItem, ExtractRefreshTaskItem +import logging + +logger = logging.getLogger('tableau.endpoint.tasks') + +class ExtractRefreshes(Endpoint): + def __init__(self, parent_srv): + super(ExtractRefreshes, self).__init__() + self.parent_srv = parent_srv + + @property + def baseurl(self): + return "{0}/sites/{1}".format(self.parent_srv.baseurl, self.parent_srv.site_id) + + def get_for_schedule(self, schedule_id, req_options=None): + url = "{0}/schedules/{1}/extracts".format(self.baseurl, schedule_id) + server_response = self.get_request(url, req_options) + pagination_item = PaginationItem.from_response(server_response.content) + tasks = ExtractRefreshTaskItem.from_response(server_response.content, schedule_id) + return tasks, pagination_item \ No newline at end of file diff --git a/tableauserverclient/server/endpoint/tasks_endpoint.py b/tableauserverclient/server/endpoint/tasks_endpoint.py new file mode 100644 index 000000000..92aaebcf8 --- /dev/null +++ b/tableauserverclient/server/endpoint/tasks_endpoint.py @@ -0,0 +1,11 @@ +from .endpoint import Endpoint +from extract_refreshes_endpoint import ExtractRefreshes +import logging + +logger = logging.getLogger('tableau.endpoint.tasks') + +class Tasks(Endpoint): + def __init__(self, parent_srv): + super(Tasks, self).__init__() + self.parent_srv = parent_srv + self.extract_refreshes = ExtractRefreshes(parent_srv) \ No newline at end of file diff --git a/tableauserverclient/server/server.py b/tableauserverclient/server/server.py index 8c28c1825..d01386fdf 100644 --- a/tableauserverclient/server/server.py +++ b/tableauserverclient/server/server.py @@ -1,5 +1,5 @@ from .exceptions import NotSignedInError -from .endpoint import Sites, Views, Users, Groups, Workbooks, Datasources, Projects, Auth, Schedules +from .endpoint import Sites, Views, Users, Groups, Workbooks, Datasources, Projects, Auth, Schedules, Tasks import requests @@ -27,6 +27,7 @@ def __init__(self, server_address): self.datasources = Datasources(self) self.projects = Projects(self) self.schedules = Schedules(self) + self.tasks = Tasks(self) def add_http_options(self, options_dict): self._http_options.update(options_dict)