New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding Schedules Support Round 2 #48
Changes from 8 commits
03e8ce3
312a1c7
b32a632
0adb03f
66e1793
0342bed
bb9ff8f
34457b3
f4490fb
79271a1
b710d9d
46cc1d0
fd17154
371b6f9
28ebb9e
c330687
d7add3a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
#### | ||
# This script demonstrates how to create schedules using the Tableau | ||
# Server Client. | ||
# | ||
# To run the script, you must have installed Python 2.7.9 or later. | ||
#### | ||
|
||
|
||
import argparse | ||
import getpass | ||
import logging | ||
|
||
from datetime import time | ||
|
||
import tableauserverclient as TSC | ||
|
||
|
||
def main(): | ||
|
||
parser = argparse.ArgumentParser(description='Creates sample schedules for each type of frequency.') | ||
parser.add_argument('--server', '-s', required=True, help='server address') | ||
parser.add_argument('--username', '-u', required=True, help='username to sign into server') | ||
parser.add_argument('--logging-level', '-l', choices=['debug', 'info', 'error'], default='error', | ||
help='desired logging level (set to error by default)') | ||
args = parser.parse_args() | ||
|
||
password = getpass.getpass("Password: ") | ||
|
||
# Set logging level based on user input, or error by default | ||
logging_level = getattr(logging, args.logging_level.upper()) | ||
logging.basicConfig(level=logging_level) | ||
|
||
tableau_auth = TSC.TableauAuth(args.username, password) | ||
server = TSC.Server(args.server) | ||
with server.auth.sign_in(tableau_auth): | ||
# Hourly Schedule | ||
hourly_interval = TSC.IntervalItem.create_hourly(time(2, 30), time(23, 0), TSC.IntervalItem.Occurrence.Hours, 2) | ||
hourly_schedule = TSC.ScheduleItem("Hourly-Schedule", 50, TSC.ScheduleItem.Type.Extract, | ||
TSC.ScheduleItem.ExecutionOrder.Parallel, hourly_interval) | ||
hourly_schedule = server.schedules.create(hourly_schedule) | ||
print("Hourly schedule created (ID: {}).".format(hourly_schedule.id)) | ||
|
||
# Daily Schedule | ||
daily_interval = TSC.IntervalItem.create_daily(time(5)) | ||
daily_schedule = TSC.ScheduleItem("Daily-Schedule", 60, TSC.ScheduleItem.Type.Subscription, | ||
TSC.ScheduleItem.ExecutionOrder.Serial, daily_interval) | ||
daily_schedule = server.schedules.create(daily_schedule) | ||
print("Daily schedule created (ID: {}).".format(daily_schedule.id)) | ||
|
||
# Weekly Schedule | ||
weekly_interval = TSC.IntervalItem.create_weekly(time(19, 15), TSC.IntervalItem.Day.Monday, | ||
TSC.IntervalItem.Day.Wednesday, TSC.IntervalItem.Day.Friday) | ||
weekly_schedule = TSC.ScheduleItem("Weekly-Schedule", 70, TSC.ScheduleItem.Type.Extract, | ||
TSC.ScheduleItem.ExecutionOrder.Serial, weekly_interval) | ||
weekly_schedule = server.schedules.create(weekly_schedule) | ||
print("Weekly schedule created (ID: {}).".format(weekly_schedule.id)) | ||
|
||
# Monthly Schedule | ||
monthly_interval = TSC.IntervalItem.create_monthly(time(23, 30), 15) | ||
monthly_schedule = TSC.ScheduleItem("Monthly-Schedule", 80, TSC.ScheduleItem.Type.Subscription, | ||
TSC.ScheduleItem.ExecutionOrder.Parallel, monthly_interval) | ||
monthly_schedule = server.schedules.create(monthly_schedule) | ||
print("Monthly schedule created (ID: {}).".format(monthly_schedule.id)) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
from .namespace import NAMESPACE | ||
from .models import ConnectionItem, DatasourceItem,\ | ||
GroupItem, PaginationItem, ProjectItem, \ | ||
GroupItem, PaginationItem, ProjectItem, ScheduleItem, \ | ||
SiteItem, TableauAuth, UserItem, ViewItem, WorkbookItem, UnpopulatedPropertyError | ||
from .server import RequestOptions, Filter, Sort, Server, ServerResponseError,\ | ||
MissingRequiredFieldError, NotSignedInError | ||
from .models.interval_item import * | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Russell didn't like it when I imported * There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. aww, but I'm lazy. ok done. |
||
|
||
__version__ = '0.0.1' | ||
__VERSION__ = __version__ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import xml.etree.ElementTree as ET | ||
from datetime import datetime | ||
from .. import NAMESPACE | ||
|
||
|
||
class IntervalItem(object): | ||
class Frequency: | ||
Hourly = "Hourly" | ||
Daily = "Daily" | ||
Weekly = "Weekly" | ||
Monthly = "Monthly" | ||
|
||
class Occurrence: | ||
Hours = "hours" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The XSD actually specifies these as lower case, I'm not sure how it was working before to be honest. But I found these by getting test failures after the refactor |
||
Minutes = "minutes" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minutes should come before hours. |
||
WeekDay = "weekDay" | ||
MonthDay = "monthDay" | ||
|
||
class Day: | ||
Sunday = "Sunday" | ||
Monday = "Monday" | ||
Tuesday = "Tuesday" | ||
Wednesday = "Wednesday" | ||
Thursday = "Thursday" | ||
Friday = "Friday" | ||
Saturday = "Saturday" | ||
LastDay = "LastDay" | ||
|
||
end_time = None | ||
frequency = None | ||
interval = None | ||
start_time = None | ||
|
||
@staticmethod | ||
def _validate_time(t): | ||
units_of_time = {"hour", "minute", "second"} | ||
|
||
if not any(hasattr(t, unit) for unit in units_of_time): | ||
error = "Invalid time object defined." | ||
raise ValueError(error) | ||
|
||
@classmethod | ||
def from_response(cls, resp, frequency): | ||
cls.from_xml_element(ET.fromstring(resp), frequency) | ||
|
||
@classmethod | ||
def from_xml_element(cls, parsed_response, frequency): | ||
start_time = parsed_response.get("start", None) | ||
start_time = datetime.strptime(start_time, "%H:%M:%S").time() | ||
end_time = parsed_response.get("end", None) | ||
if end_time is not None: | ||
end_time = datetime.strptime(end_time, "%H:%M:%S").time() | ||
interval_elems = parsed_response.findall(".//t:intervals/t:interval", namespaces=NAMESPACE) | ||
interval = [] | ||
for interval_elem in interval_elems: | ||
interval.extend(interval_elem.attrib.items()) | ||
|
||
# If statement of doom until I think of a better way | ||
|
||
if frequency == IntervalItem.Frequency.Daily: | ||
return DailyInterval(start_time) | ||
|
||
if frequency == IntervalItem.Frequency.Hourly: | ||
interval_occurrence, interval_value = interval.pop() | ||
return HourlyInterval(start_time, end_time, interval_occurrence, interval_value) | ||
|
||
if frequency == IntervalItem.Frequency.Weekly: | ||
interval_values = [i[1] for i in interval] | ||
return WeeklyInterval(start_time, *interval_values) | ||
|
||
if frequency == IntervalItem.Frequency.Monthly: | ||
interval_occurrence, interval_value = interval.pop() | ||
return MonthlyInterval(start_time, interval_value) | ||
|
||
|
||
class HourlyInterval(IntervalItem): | ||
def __init__(self, start_time, end_time, interval_occurrence, interval_value): | ||
self._validate_time(start_time) | ||
self._validate_time(end_time) | ||
|
||
if interval_occurrence != IntervalItem.Occurrence.Hours and \ | ||
interval_occurrence != IntervalItem.Occurrence.Minutes: | ||
error = "Invalid interval type defined: {}.".format(interval_occurrence) | ||
raise ValueError(error) | ||
elif interval_occurrence == IntervalItem.Occurrence.Hours and int(interval_value) not in [1, 2, 4, 6, 8, 12]: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly I'm not sure how this worked before, parsed responses will be strings, found via test failures. |
||
error = "Invalid hour value defined: {}.".format(interval_value) | ||
raise ValueError(error) | ||
elif interval_occurrence == IntervalItem.Occurrence.Minutes and int(interval_value) not in [15, 30]: | ||
error = "Invalid minute value defined: {}".format(interval_value) | ||
raise ValueError(error) | ||
|
||
self.start_time = start_time | ||
self.end_time = end_time | ||
self.frequency = IntervalItem.Frequency.Hourly | ||
self.interval = [(interval_occurrence.lower(), str(interval_value))] | ||
|
||
|
||
class DailyInterval(IntervalItem): | ||
def __init__(self, start_time, *args): | ||
self._validate_time(start_time) | ||
|
||
self.start_time = start_time | ||
self.frequency = IntervalItem.Frequency.Daily | ||
|
||
|
||
class WeeklyInterval(IntervalItem): | ||
def __init__(self, start_time, *interval_values): | ||
self._validate_time(start_time) | ||
if not all(hasattr(IntervalItem.Day, day) for day in interval_values): | ||
raise ValueError("Invalid week day defined " + str(interval_values)) | ||
self.start_time = start_time | ||
self.frequency = IntervalItem.Frequency.Weekly | ||
self.interval = [(IntervalItem.Occurrence.WeekDay, day) for day in interval_values] | ||
|
||
|
||
class MonthlyInterval(IntervalItem): | ||
def __init__(self, start_time, interval_value): | ||
self._validate_time(start_time) | ||
|
||
if (int(interval_value) < 1 or int(interval_value) > 31) and interval_value != IntervalItem.Day.LastDay: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, not sure how this worked without converting to an int before |
||
error = "Invalid interval value defined for a monthly frequency: {}.".format(interval_value) | ||
raise ValueError(error) | ||
|
||
self.start_time = start_time | ||
self.frequency = IntervalItem.Frequency.Monthly | ||
self.interval = [(IntervalItem.Occurrence.MonthDay, str(interval_value))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yikes, I need to update these now :)
(I wonder if we can find a way to run these, either in a local task, or in travis somehow)