Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,9 @@ python setup.py test
Our CI runs include a Python lint run, so you should run this locally and fix complaints before committing as this will fail your checkin.

```shell
pycodestyle tableauserverclient test samples
# this will run the formatter without making changes
black --line-length 120 tableauserverclient --check

# this will format the directory and code for you
black --line-length 120 tableauserverclient
```
1 change: 1 addition & 0 deletions tableauserverclient/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
ConnectionItem,
DataAlertItem,
DatasourceItem,
DQWItem,
GroupItem,
JobItem,
BackgroundJobItem,
Expand Down
23 changes: 20 additions & 3 deletions tableauserverclient/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=
dispcmd = str([c] + args)
# remember shell=False, so use git.cmd on windows, not just git
p = subprocess.Popen(
[c] + args, cwd=cwd, env=env, stdout=subprocess.PIPE, stderr=(subprocess.PIPE if hide_stderr else None)
[c] + args,
cwd=cwd,
env=env,
stdout=subprocess.PIPE,
stderr=(subprocess.PIPE if hide_stderr else None),
)
break
except EnvironmentError:
Expand Down Expand Up @@ -243,7 +247,17 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
# if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
# if there isn't one, this yields HEX[-dirty] (no NUM)
describe_out, rc = run_command(
GITS, ["describe", "--tags", "--dirty", "--always", "--long", "--match", "%s*" % tag_prefix], cwd=root
GITS,
[
"describe",
"--tags",
"--dirty",
"--always",
"--long",
"--match",
"%s*" % tag_prefix,
],
cwd=root,
)
# --long was added in git-1.5.5
if describe_out is None:
Expand Down Expand Up @@ -285,7 +299,10 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
if verbose:
fmt = "tag '%s' doesn't start with prefix '%s'"
print(fmt % (full_tag, tag_prefix))
pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % (full_tag, tag_prefix)
pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % (
full_tag,
tag_prefix,
)
return pieces
pieces["closest-tag"] = full_tag[len(tag_prefix) :]

Expand Down
9 changes: 8 additions & 1 deletion tableauserverclient/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@
from .data_alert_item import DataAlertItem
from .datasource_item import DatasourceItem
from .database_item import DatabaseItem
from .dqw_item import DQWItem
from .exceptions import UnpopulatedPropertyError
from .favorites_item import FavoriteItem
from .group_item import GroupItem
from .flow_item import FlowItem
from .interval_item import IntervalItem, DailyInterval, WeeklyInterval, MonthlyInterval, HourlyInterval
from .interval_item import (
IntervalItem,
DailyInterval,
WeeklyInterval,
MonthlyInterval,
HourlyInterval,
)
from .job_item import JobItem, BackgroundJobItem
from .pagination_item import PaginationItem
from .project_item import ProjectItem
Expand Down
6 changes: 5 additions & 1 deletion tableauserverclient/models/data_alert_item.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import xml.etree.ElementTree as ET

from .property_decorators import property_not_empty, property_is_enum, property_is_boolean
from .property_decorators import (
property_not_empty,
property_is_enum,
property_is_boolean,
)
from .user_item import UserItem
from .view_item import ViewItem

Expand Down
24 changes: 22 additions & 2 deletions tableauserverclient/models/database_item.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import xml.etree.ElementTree as ET

from .property_decorators import property_is_enum, property_not_empty, property_is_boolean
from .property_decorators import (
property_is_enum,
property_not_empty,
property_is_boolean,
)
from .exceptions import UnpopulatedPropertyError


Expand Down Expand Up @@ -34,8 +38,17 @@ def __init__(self, name, description=None, content_permissions=None):
self._permissions = None
self._default_table_permissions = None

self._data_quality_warnings = None

self._tables = None # Not implemented yet

@property
def dqws(self):
if self._data_quality_warnings is None:
error = "Project item must be populated with permissions first."
raise UnpopulatedPropertyError(error)
return self._data_quality_warnings()

@property
def content_permissions(self):
return self._content_permissions
Expand Down Expand Up @@ -229,7 +242,14 @@ def _set_tables(self, tables):
self._tables = tables

def _set_default_permissions(self, permissions, content_type):
setattr(self, "_default_{content}_permissions".format(content=content_type), permissions)
setattr(
self,
"_default_{content}_permissions".format(content=content_type),
permissions,
)

def _set_data_quality_warnings(self, dqw):
self._data_quality_warnings = dqw

@classmethod
def from_response(cls, resp, ns):
Expand Down
17 changes: 16 additions & 1 deletion tableauserverclient/models/datasource_item.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import xml.etree.ElementTree as ET
from .exceptions import UnpopulatedPropertyError
from .property_decorators import property_not_nullable, property_is_boolean, property_is_enum
from .property_decorators import (
property_not_nullable,
property_is_boolean,
property_is_enum,
)
from .tag_item import TagItem
from ..datetime_helpers import parse_datetime
import copy
Expand Down Expand Up @@ -35,6 +39,7 @@ def __init__(self, project_id, name=None):
self.tags = set()

self._permissions = None
self._data_quality_warnings = None

@property
def ask_data_enablement(self):
Expand Down Expand Up @@ -94,6 +99,13 @@ def encrypt_extracts(self):
def encrypt_extracts(self, value):
self._encrypt_extracts = value

@property
def dqws(self):
if self._data_quality_warnings is None:
error = "Project item must be populated with dqws first."
raise UnpopulatedPropertyError(error)
return self._data_quality_warnings()

@property
def has_extracts(self):
return self._has_extracts
Expand Down Expand Up @@ -142,6 +154,9 @@ def _set_connections(self, connections):
def _set_permissions(self, permissions):
self._permissions = permissions

def _set_data_quality_warnings(self, dqws):
self._data_quality_warnings = dqws

def _parse_common_elements(self, datasource_xml, ns):
if not isinstance(datasource_xml, ET.Element):
datasource_xml = ET.fromstring(datasource_xml).find(".//t:datasource", namespaces=ns)
Expand Down
148 changes: 148 additions & 0 deletions tableauserverclient/models/dqw_item.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import xml.etree.ElementTree as ET
from ..datetime_helpers import parse_datetime


class DQWItem(object):
class WarningType:
WARNING = "WARNING"
DEPRECATED = "DEPRECATED"
STALE = "STALE"
SENSITIVE_DATA = "SENSITIVE_DATA"
MAINTENANCE = "MAINTENANCE"

def __init__(self, warning_type="WARNING", message=None, active=True, severe=False):
self._id = None
# content related
self._content_id = None
self._content_type = None

# DQW related
self.warning_type = warning_type
self.message = message
self.active = active
self.severe = severe
self._created_at = None
self._updated_at = None

# owner
self._owner_display_name = None
self._owner_id = None

@property
def id(self):
return self._id

@property
def content_id(self):
return self._content_id

@property
def content_type(self):
return self._content_type

@property
def owner_display_name(self):
return self._owner_display_name

@property
def owner_id(self):
return self._owner_id

@property
def warning_type(self):
return self._warning_type

@warning_type.setter
def warning_type(self, value):
self._warning_type = value

@property
def message(self):
return self._message

@message.setter
def message(self, value):
self._message = value

@property
def active(self):
return self._active

@active.setter
def active(self, value):
self._active = value

@property
def severe(self):
return self._severe

@severe.setter
def severe(self, value):
self._severe = value

@property
def active(self):
return self._active

@active.setter
def active(self, value):
self._active = value

@property
def created_at(self):
return self._created_at

@created_at.setter
def created_at(self, value):
self._created_at = value

@property
def updated_at(self):
return self._updated_at

@updated_at.setter
def updated_at(self, value):
self._updated_at = value

@classmethod
def from_response(cls, resp, ns):
return cls.from_xml_element(ET.fromstring(resp), ns)

@classmethod
def from_xml_element(cls, parsed_response, ns):
all_dqws = []
dqw_elem_list = parsed_response.findall(".//t:dataQualityWarning", namespaces=ns)
for dqw_elem in dqw_elem_list:
dqw = DQWItem()
dqw._id = dqw_elem.get("id", None)
dqw._owner_display_name = dqw_elem.get("userDisplayName", None)
dqw._content_id = dqw_elem.get("contentId", None)
dqw._content_type = dqw_elem.get("contentType", None)
dqw.message = dqw_elem.get("message", None)
dqw.warning_type = dqw_elem.get("type", None)

is_active = dqw_elem.get("isActive", None)
if is_active is not None:
dqw._active = string_to_bool(is_active)

is_severe = dqw_elem.get("isSevere", None)
if is_severe is not None:
dqw._severe = string_to_bool(is_severe)

dqw._created_at = parse_datetime(dqw_elem.get("createdAt", None))
dqw._updated_at = parse_datetime(dqw_elem.get("updatedAt", None))

owner_id = None
owner_tag = dqw_elem.find(".//t:owner", namespaces=ns)
if owner_tag is not None:
owner_id = owner_tag.get("id", None)
dqw._owner_id = owner_id

all_dqws.append(dqw)

return all_dqws


# Used to convert string represented boolean to a boolean type
def string_to_bool(s):
return s.lower() == "true"
Loading