Skip to content
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

Add support for all types of monthly repeating schedules #1462

Merged
merged 4 commits into from
Sep 17, 2024

Conversation

bcantoni
Copy link
Contributor

@bcantoni bcantoni commented Sep 13, 2024

This should fix handling all of the "monthly" schedule types now; see details in #1358 and #1369.

Previously we just handled monthly schedules which repeated on a day (1-31)
or 'LastDay'. Tableau Server has since added more options such as first
Monday. This change catches up the interval validation to match what might
be received from the server.

Fixes #1358
Copy link

github-actions bot commented Sep 13, 2024

Coverage

Coverage Report
FileStmtsMissCoverMissing
tableauserverclient
   __init__.py50100% 
   _version.py278163163 41%
   config.py130100% 
   datetime_helpers.py2511 96%
   exponential_backoff.py200100% 
   filesys_helpers.py310100% 
   namespace.py2633 88%
tableauserverclient/helpers
   __init__.py10100% 
   headers.py1388 38%
   logging.py20100% 
   strings.py2711 96%
tableauserverclient/models
   __init__.py410100% 
   column_item.py553232 42%
   connection_credentials.py351111 69%
   connection_item.py771010 87%
   custom_view_item.py11999 92%
   data_acceleration_report_item.py5411 98%
   data_alert_item.py15933 98%
   data_freshness_policy_item.py1561515 90%
   database_item.py2073636 83%
   datasource_item.py2471111 96%
   dqw_item.py10455 95%
   exceptions.py40100% 
   favorites_item.py5788 86%
   fileupload_item.py190100% 
   flow_item.py1491010 93%
   flow_run_item.py720100% 
   group_item.py8133 96%
   groupset_item.py3511 97%
   interval_item.py1823232 82%
   job_item.py1871010 95%
   linked_tasks_item.py8011 99%
   metric_item.py1301212 91%
   pagination_item.py340100% 
   permissions_item.py1051212 89%
   project_item.py1453131 79%
   property_decorators.py991818 82%
   reference_item.py2311 96%
   revision_item.py600100% 
   schedule_item.py20666 97%
   server_info_item.py3955 87%
   site_item.py6101212 98%
   subscription_item.py10333 97%
   table_item.py1181818 85%
   tableau_auth.py612424 61%
   tableau_types.py2511 96%
   tag_item.py160100% 
   target.py60100% 
   task_item.py5722 96%
   user_item.py2771717 94%
   view_item.py1781515 92%
   virtual_connection_item.py6388 87%
   webhook_item.py660100% 
   workbook_item.py2751616 94%
tableauserverclient/server
   __init__.py90100% 
   exceptions.py40100% 
   filter.py2111 95%
   pager.py3633 92%
   query.py1291313 90%
   request_factory.py1089148148 86%
   request_options.py26944 99%
   server.py1671717 90%
   sort.py60100% 
tableauserverclient/server/endpoint
   __init__.py330100% 
   auth_endpoint.py761212 84%
   custom_views_endpoint.py11877 94%
   data_acceleration_report_endpoint.py210100% 
   data_alert_endpoint.py972525 74%
   databases_endpoint.py1093030 72%
   datasources_endpoint.py3103636 88%
   default_permissions_endpoint.py4655 89%
   dqw_endpoint.py431616 63%
   endpoint.py1812020 89%
   exceptions.py6066 90%
   favorites_endpoint.py952222 77%
   fileuploads_endpoint.py510100% 
   flow_runs_endpoint.py661111 83%
   flow_task_endpoint.py2333 87%
   flows_endpoint.py2155858 73%
   groups_endpoint.py1261010 92%
   groupsets_endpoint.py7488 89%
   jobs_endpoint.py711212 83%
   linked_tasks_endpoint.py380100% 
   metadata_endpoint.py881414 84%
   metrics_endpoint.py5888 86%
   permissions_endpoint.py4755 89%
   projects_endpoint.py1252020 84%
   resource_tagger.py1344343 68%
   schedules_endpoint.py1001111 89%
   server_info_endpoint.py351010 71%
   sites_endpoint.py1262828 78%
   subscriptions_endpoint.py581515 74%
   tables_endpoint.py1073636 66%
   tasks_endpoint.py6577 89%
   users_endpoint.py1301616 88%
   views_endpoint.py1371010 93%
   virtual_connections_endpoint.py1141111 90%
   webhooks_endpoint.py571111 81%
   workbooks_endpoint.py3322727 92%
TOTAL10553131488% 

@bcantoni
Copy link
Contributor Author

@jacalata and/or @jorwoods if you could help review that would be great.

raise ValueError(error)
try:
if not (1 <= int(interval_value) <= 31):
error = f"Invalid monthly numeric frequency interval: {interval_value}."
Copy link

@anyoung-tableau anyoung-tableau Sep 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error message can never happen right? Its ValueError will get caught below and either swallowed or a new ValueError will be thrown. I haven't used Python much, is it common to use exceptions to control flow?

Maybe use the isinstance(interval_value, (int, float)) construct like on line 80 to detect if it's a number first.

Or if it's always a string, interval_value.isdigit()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well in theory I think it would fail right there if the numerical value was 32 or "32".

I agree using exceptions here is a little funky. I was trying to fit in with the methods used already.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using exceptions for control flow isn't uncommon in python. The except ValueError on 274 would indeed catch and consume the one raised on 273. The type check proposed by @anyoung-tableau does seem like a good alternative to let the different errors surface.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take another look at it. The challenge is that even numbers are strings by the time they land here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Easiest way to do it seems like explicitly listing the known allowed digits:
for value in range(1, 32):
VALID_INTERVALS.add(str(value))

raise ValueError(error)
try:
if not (1 <= int(interval_value) <= 31):
error = f"Invalid monthly numeric frequency interval: {interval_value}."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Easiest way to do it seems like explicitly listing the known allowed digits:
for value in range(1, 32):
VALID_INTERVALS.add(str(value))

@bcantoni
Copy link
Contributor Author

@jacalata I modified the validation and simplified by including the 1..31 range as both int or string. Looks better now and avoids that "never reachable" error path I started with.

Copy link
Contributor

@jacalata jacalata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good

@bcantoni bcantoni merged commit 52c1541 into development Sep 17, 2024
25 of 26 checks passed
@bcantoni bcantoni deleted the bcantoni/1358-schedule-intervals branch September 17, 2024 20:24
jacalata pushed a commit that referenced this pull request Sep 17, 2024
Previously we just handled monthly schedules which repeated on a day (1-31)
or 'LastDay'. Tableau Server has since added more options such as "first
Monday". This change catches up the interval validation to match what might
be received from the server.

Fixes #1358

* Add failing test for "monthly on first Monday" schedule
* Add support for all monthly schedule variations
* Unrelated fix for debug logging of API responses and add a small warning
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants