8000 Adding Schedules Support (#48) · Souravsp/server-client-python@5dbdbee · GitHub
[go: up one dir, main page]

Skip to content

Commit 5dbdbee

Browse files
authored
Adding Schedules Support (tableau#48)
Original PR by @shinchris tableau#30 * added ability to query and delete schedules * added ability to create and update schedules * intervals for schedules are expressed as (Unit)Interval classes * hourly intervals can take .25 and .5 to represent 15 and 30 minute schedules
1 parent ab6b664 commit 5dbdbee

19 files changed

+902
-6
lines changed

samples/create_schedules.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
####
2+
# This script demonstrates how to create schedules using the Tableau
3+
# Server Client.
4+
#
5+
# To run the script, you must have installed Python 2.7.9 or later.
6+
####
7+
8+
9+
import argparse
10+
import getpass
11+
import logging
12+
13+
from datetime import time
14+
15+
import tableauserverclient as TSC
16+
17+
18+
def main():
19+
20+
parser = argparse.ArgumentParser(description='Creates sample schedules for each type of frequency.')
21+
parser.add_argument('--server', '-s', required=True, help='server address')
22+
parser.add_argument('--username', '-u', required=True, help='username to sign into server')
23+
parser.add_argument('--logging-level', '-l', choices=['debug', 'info', 'error'], default='error',
24+
help='desired logging level (set to error by default)')
25+
args = parser.parse_args()
26+
27+
password = getpass.getpass("Password: ")
28+
29+
# Set logging level based on user input, or error by default
30+
logging_level = getattr(logging, args.logging_level.upper())
31+
logging.basicConfig(level=logging_level)
32+
33+
tableau_auth = TSC.TableauAuth(args.username, password)
34+
server = TSC.Server(args.server)
35+
with server.auth.sign_in(tableau_auth):
36+
# Hourly Schedule
37+
# This schedule will run every 2 hours between 2:30AM and 11:00PM
38+
hourly_interval = TSC.HourlyInterval(start_time=time(2, 30),
39+
end_time=time(23, 0),
40+
interval_value=2)
41+
42+
hourly_schedule = TSC.ScheduleItem("Hourly-Schedule", 50, TSC.ScheduleItem.Type.Extract,
43+
TSC.ScheduleItem.ExecutionOrder.Parallel, hourly_interval)
44+
hourly_schedule = server.schedules.create(hourly_schedule)
45+
print("Hourly schedule created (ID: {}).".format(hourly_schedule.id))
46+
47+
# Daily Schedule
48+
# This schedule will run every day at 5AM
49+
daily_interval = TSC.DailyInterval(start_time=time(5))
50+
daily_schedule = TSC.ScheduleItem("Daily-Schedule", 60, TSC.ScheduleItem.Type.Subscription,
51+
TSC.ScheduleItem.ExecutionOrder.Serial, daily_interval)
52+
daily_schedule = server.schedules.create(daily_schedule)
53+
print("Daily schedule created (ID: {}).".format(daily_schedule.id))
54+
55+
# Weekly Schedule
56+
# This schedule will wun every Monday, Wednesday, and Friday at 7:15PM
57+
weekly_interval = TSC.WeeklyInterval(time(19, 15),
58+
TSC.IntervalItem.Day.Monday,
59+
TSC.IntervalItem.Day.Wednesday,
60+
TSC.IntervalItem.Day.Friday)
61+
weekly_schedule = TSC.ScheduleItem("Weekly-Schedule", 70, TSC.ScheduleItem.Type.Extract,
62+
TSC.ScheduleItem.ExecutionOrder.Serial, weekly_interval)
63+
weekly_schedule = server.schedules.create(weekly_schedule)
64+
print("Weekly schedule created (ID: {}).".format(weekly_schedule.id))
65+
66+
# Monthly Schedule
67+
# This schedule will run on the 15th of every month at 11:30PM
68+
monthly_interval = TSC.MonthlyInterval(start_time=time(23, 30),
69+
interval_value=15)
70+
monthly_schedule = TSC.ScheduleItem("Monthly-Schedule", 80, TSC.ScheduleItem.Type.Subscription,
71+
TSC.ScheduleItem.ExecutionOrder.Parallel, monthly_interval)
72+
monthly_schedule = server.schedules.create(monthly_schedule)
73+
print("Monthly schedule created (ID: {}).".format(monthly_schedule.id))
74+
75+
76+
if __name__ == '__main__':
77+
main()

tableauserverclient/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from .namespace import NAMESPACE
22
from .models import ConnectionItem, DatasourceItem,\
3-
GroupItem, PaginationItem, ProjectItem, \
4-
SiteItem, TableauAuth, UserItem, ViewItem, WorkbookItem, UnpopulatedPropertyError
3+
GroupItem, PaginationItem, ProjectItem, ScheduleItem, \
4+
SiteItem, TableauAuth, UserItem, ViewItem, WorkbookItem, UnpopulatedPropertyError, \
5+
HourlyInterval, DailyInterval, WeeklyInterval, MonthlyInterval, IntervalItem
56
from .server import RequestOptions, Filter, Sort, Server, ServerResponseError,\
67
MissingRequiredFieldError, NotSignedInError
78

tableauserverclient/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
from .datasource_item import DatasourceItem
33
from .exceptions import UnpopulatedPropertyError
44
from .group_item import GroupItem
5+
from .interval_item import IntervalItem, DailyInterval, WeeklyInterval, MonthlyInterval, HourlyInterval
56
from .pagination_item import PaginationItem
67
from .project_item import ProjectItem
8+
from .schedule_item import ScheduleItem
79
from .site_item import SiteItem
810
from .tableau_auth import TableauAuth
911
from .user_item import UserItem
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
from .property_decorators import property_is_valid_time, property_not_nullable
2+
3+
4+
class IntervalItem(object):
5+
class Frequency:
6+
Hourly = "Hourly"
7+
Daily = "Daily"
8+
Weekly = "Weekly"
9+
Monthly = "Monthly"
10+
11+
class Occurrence:
12+
Minutes = "minutes"
13+
Hours = "hours"
14+
WeekDay = "weekDay"
15+
MonthDay = "monthDay"
16+
17+
class Day:
18+
Sunday = "Sunday"
19+
Monday = "Monday"
20+
Tuesday = "Tuesday"
21+
Wednesday = "Wednesday"
22+
Thursday = "Thursday"
23+
Friday = "Friday"
24+
Saturday = "Saturday"
25+
LastDay = "LastDay"
26+
27+
28+
class HourlyInterval(object):
29+
def __init__(self, start_time, end_time, interval_value):
30+
31+
self.start_time = start_time
32+
self.end_time = end_time
33+
self.interval = interval_value
34+
35+
@property
36+
def _frequency(self):
37+
return IntervalItem.Frequency.Hourly
38+
39+
@property
40+
def start_time(self):
41+
return self._start_time
42+
43+
@start_time.setter
44+
@property_is_valid_time
45+
@property_not_nullable
46+
def start_time(self, value):
47+
self._start_time = value
48+
49+
@property
50+
def end_time(self):
51+
return self._end_time
52+
53+
@end_time.setter
54+
@property_is_valid_time
55+
@property_not_nullable
56+
def end_time(self, value):
57+
self._end_time = value
58+
59+
@property
60+
def interval(self):
61+
return self._interval
62+
63+
@interval.setter
64+
def interval(self, interval):
65+
VALID_INTERVALS = {.25, .5, 1, 2, 4, 6, 8, 12}
66+
if float(interval) not in VALID_INTERVALS:
67+
error = "Invalid interval {} not in {}".format(interval, str(VALID_INTERVALS))
68+
raise ValueError(error)
69+
70+
self._interval = interval
71+
72+
def _interval_type_pairs(self):
73+
74+
# We use fractional hours for the two minute-based intervals.
75+
# Need to convert to minutes from hours here
76+
if self.interval in {.25, .5}:
77+
calculated_interval = int(self.interval * 60)
78+
interval_type = IntervalItem.Occurrence.Minutes
79+
else:
80+
calculated_interval = self.interval
81+
interval_type = IntervalItem.Occurrence.Hours
82+
83+
return [(interval_type, str(calculated_interval))]
84+
85+
86+
class DailyInterval(object):
87+
def __init__(self, start_time):
88+
self.start_time = start_time
89+
90+
@property
91+
def _frequency(self):
92+
return IntervalItem.Frequency.Daily
93+
94+
@property
95+
def start_time(self):
96+
return self._start_time
97+
98+
@start_time.setter
99+
@property_is_valid_time
100+
@property_not_nullable
101+
def start_time(self, value):
102+
self._start_time = value
103+
104+
105+
class WeeklyInterval(object):
106+
def __init__(self, start_time, *interval_values):
107+
self.start_time = start_time
108+
self.interval = interval_values
109+
110+
@property
111+
def _frequency(self):
112+
return IntervalItem.Frequency.Weekly
113+
114+
@property
115+
def start_time(self):
116+
return self._start_time
117+
118+
@start_time.setter
119+
@property_is_valid_time
120+
@property_not_nullable
121+
def start_time(self, value):
122+
self._start_time = value
123+
124+
@property
125+
def interval(self):
126+
return self._interval
127+
128+
@interval.setter
129+
def interval(self, interval_values):
130+
if not all(hasattr(IntervalItem.Day, day) for day in interval_values):
131+
raise ValueError("Invalid week day defined " + str(interval_values))
132+
133+
self._interval = interval_values
134+
135+
def _interval_type_pairs(self):
136+
return [(IntervalItem.Occurrence.WeekDay, day) for day in self.interval]
137+
138+
139+
class MonthlyInterval(object):
140+
def __init__(self, start_time, interval_value):
141+
self.start_time = start_time
142+
self.interval = str(interval_value)
143+
144+
@property
145+
def _frequency(self):
146+
return IntervalItem.Frequency.Monthly
147+
148+
@property
149+
def start_time(self):
150+
return self._start_time
151+
152+
@start_time.setter
153+
@property_is_valid_time
154+
@property_not_nullable
155+
def start_time(self, value):
156+
self._start_time = value
157+
158+
@property
159+
def interval(self):
160+
return self._interval
161+
162+
@interval.setter
163+
def interval(self, interval_value):
164+
error = "Invalid interval value for a monthly frequency: {}.".format(interval_value)
165+
166+
# This is weird because the value could be a str or an int
167+
# The only valid str is 'LastDay' so we check that first. If that's not it
168+
# try to convert it to an int, if that fails because it's an incorrect string
169+
# like 'badstring' we catch and re-raise. Otherwise we convert to int and check
170+
# that it's in range 1-31
171+
172+
if interval_value != "LastDay":
173+
try:
174+
if not (1 <= int(interval_value) <= 31):
175+
raise ValueError(error)
176+
except ValueError as e:
177+
if interval_value != "LastDay":
178+
raise ValueError(error)
179+
180+
self._interval = str(interval_value)
181+
182+
def _interval_type_pairs(self):
183+
return [(IntervalItem.Occurrence.MonthDay, self.interval)]

tableauserverclient/models/property_decorators.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,41 @@ def wrapper(self, value):
4646
return func(self, value)
4747

4848
return wrapper
49+
50+
51+
def property_is_valid_time(func):
52+
@wraps(func)
53+
def wrapper(self, value):
54+
units_of_time = {"hour", "minute", "second"}
55+
56+
if not any(hasattr(value, unit) for unit in units_of_time):
57+
error = "Invalid time object defined."
58+
raise ValueError(error)
59+
return func(self, value)
60+
61+
return wrapper
62+
63+
64+
def property_is_int(range):
65+
def property_type_decorator(func):
66+
@wraps(func)
67+
def wrapper(self, value):
68+
error = "Invalid priority defined: {}.".format(value)
69+
70+
if range is None:
71+
if isinstance(value, int):
72+
return func(self, value)
73+
else:
74+
raise ValueError(error)
75+
76+
min, max = range
77+
78+
if value < min or value > max:
79+
80+
raise ValueError(error)
81+
82+
return func(self, value)
83+
84+
return wrapper
85+
86+
return property_type_decorator

0 commit comments

Comments
 (0)
0