Skip to content

Commit 114955e

Browse files
committed
feat: add support for dataset.default_rounding_mode
1 parent 535c982 commit 114955e

File tree

3 files changed

+152
-25
lines changed

3 files changed

+152
-25
lines changed

google/cloud/bigquery/dataset.py

+34-17
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ class Dataset(object):
527527
"friendly_name": "friendlyName",
528528
"default_encryption_configuration": "defaultEncryptionConfiguration",
529529
"storage_billing_model": "storageBillingModel",
530-
"max_time_travel_hours": "maxTimeTravelHours",
530+
"default_rounding_mode": "defaultRoundingMode",
531531
}
532532

533533
def __init__(self, dataset_ref) -> None:
@@ -536,25 +536,42 @@ def __init__(self, dataset_ref) -> None:
536536
self._properties = {"datasetReference": dataset_ref.to_api_repr(), "labels": {}}
537537

538538
@property
539-
def max_time_travel_hours(self):
540-
"""
541-
Optional[int]: Defines the time travel window in hours. The value can be from 48 to 168 hours (2 to 7 days).
542-
value must in multiple of 24 hours (48, 72, 96, 120, 144, 168)
543-
The default value is 168 hours if this is not set.
544-
"""
545-
return self._properties.get("maxTimeTravelHours")
539+
def default_rounding_mode(self):
540+
"""Union[str, None]: defaultRoundingMode of the dataset as set by the user
541+
(defaults to :data:`None`).
542+
543+
Set the value to one of ``'ROUND_HALF_AWAY_FROM_ZERO'``, ``'ROUND_HALF_EVEN'``, or
544+
``'ROUNDING_MODE_UNSPECIFIED'``.
546545
547-
@max_time_travel_hours.setter
548-
def max_time_travel_hours(self, hours):
549-
if not isinstance(hours, int):
550-
raise ValueError("Pass the int value")
551-
if hours < 2 * 24 or hours > 7 * 24:
546+
See `default rounding mode
547+
<https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets#Dataset.FIELDS.default_rounding_mode>`_
548+
in REST API docs and `updating the default rounding model
549+
<https://cloud.google.com/bigquery/docs/updating-datasets#update_rounding_mode>`_
550+
guide.
551+
552+
Raises:
553+
ValueError: for invalid value types.
554+
555+
"""
556+
return self._properties.get("defaultRoundingMode")
557+
558+
@default_rounding_mode.setter
559+
def default_rounding_mode(self, value):
560+
possible_values = [
561+
"ROUNDING_MODE_UNSPECIFIED",
562+
"ROUND_HALF_AWAY_FROM_ZERO",
563+
"ROUND_HALF_EVEN",
564+
]
565+
if not isinstance(value, str) and value is not None:
566+
raise ValueError("Pass a string, or None")
567+
if value is None:
568+
self._properties["defaultRoundingMode"] = "ROUNDING_MODE_UNSPECIFIED"
569+
if value not in possible_values and value is not None:
552570
raise ValueError(
553-
"Time Travel Window should be from 48 to 168 hours (2 to 7 days)"
571+
f'rounding mode needs to be one of {",".join(possible_values)}'
554572
)
555-
if hours % 24 != 0:
556-
raise ValueError("Time Travel Window should be multiple of 24")
557-
self._properties["maxTimeTravelHours"] = hours
573+
if value:
574+
self._properties["defaultRoundingMode"] = value
558575

559576
@property
560577
def project(self):

tests/system/test_client.py

+9-7
Original file line numberDiff line numberDiff line change
@@ -238,11 +238,6 @@ def test_create_dataset(self):
238238
self.assertEqual(dataset.dataset_id, DATASET_ID)
239239
self.assertEqual(dataset.project, Config.CLIENT.project)
240240

241-
def test_create_dataset_max_time_travel_hours(self):
242-
DATASET_ID = _make_dataset_id("create_ci_dataset")
243-
dataset = self.temp_dataset(DATASET_ID, max_time_travel_hours=24 * 2)
244-
self.assertEqual(int(dataset.max_time_travel_hours), 24 * 2)
245-
246241
def test_get_dataset(self):
247242
dataset_id = _make_dataset_id("get_dataset")
248243
client = Config.CLIENT
@@ -270,6 +265,13 @@ def test_get_dataset(self):
270265
self.assertEqual(got.friendly_name, "Friendly")
271266
self.assertEqual(got.description, "Description")
272267

268+
def test_create_dataset_with_default_rounding_mode(self):
269+
DATASET_ID = _make_dataset_id("create_dataset_rounding_mode")
270+
dataset = self.temp_dataset(DATASET_ID, default_rounding_mode="ROUND_HALF_EVEN")
271+
272+
self.assertTrue(_dataset_exists(dataset))
273+
self.assertEqual(dataset.default_rounding_mode, "ROUND_HALF_EVEN")
274+
273275
def test_update_dataset(self):
274276
dataset = self.temp_dataset(_make_dataset_id("update_dataset"))
275277
self.assertTrue(_dataset_exists(dataset))
@@ -2297,8 +2299,8 @@ def temp_dataset(self, dataset_id, *args, **kwargs):
22972299
dataset = Dataset(dataset_ref)
22982300
if kwargs.get("location"):
22992301
dataset.location = kwargs.get("location")
2300-
if kwargs.get("max_time_travel_hours"):
2301-
dataset.max_time_travel_hours = kwargs.get("max_time_travel_hours")
2302+
if kwargs.get("default_rounding_mode"):
2303+
dataset.default_rounding_mode = kwargs.get("default_rounding_mode")
23022304
dataset = helpers.retry_403(Config.CLIENT.create_dataset)(dataset)
23032305
self.to_delete.append(dataset)
23042306
return dataset

tests/unit/test_create_dataset.py

+109-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ def test_create_dataset_w_attrs(client, PROJECT, DS_ID):
6363
"datasetId": "starry-skies",
6464
"tableId": "northern-hemisphere",
6565
}
66+
DEFAULT_ROUNDING_MODE = "ROUND_HALF_EVEN"
6667
RESOURCE = {
6768
"datasetReference": {"projectId": PROJECT, "datasetId": DS_ID},
6869
"etag": "etag",
@@ -73,6 +74,7 @@ def test_create_dataset_w_attrs(client, PROJECT, DS_ID):
7374
"defaultTableExpirationMs": "3600",
7475
"labels": LABELS,
7576
"access": [{"role": "OWNER", "userByEmail": USER_EMAIL}, {"view": VIEW}],
77+
"defaultRoundingMode": DEFAULT_ROUNDING_MODE,
7678
}
7779
conn = client._connection = make_connection(RESOURCE)
7880
entries = [
@@ -88,8 +90,9 @@ def test_create_dataset_w_attrs(client, PROJECT, DS_ID):
8890
before.default_table_expiration_ms = 3600
8991
before.location = LOCATION
9092
before.labels = LABELS
93+
before.default_rounding_mode = DEFAULT_ROUNDING_MODE
9194
after = client.create_dataset(before)
92-
95+
print(after)
9396
assert after.dataset_id == DS_ID
9497
assert after.project == PROJECT
9598
assert after.etag == RESOURCE["etag"]
@@ -99,6 +102,7 @@ def test_create_dataset_w_attrs(client, PROJECT, DS_ID):
99102
assert after.location == LOCATION
100103
assert after.default_table_expiration_ms == 3600
101104
assert after.labels == LABELS
105+
assert after.default_rounding_mode == DEFAULT_ROUNDING_MODE
102106

103107
conn.api_request.assert_called_once_with(
104108
method="POST",
@@ -109,6 +113,7 @@ def test_create_dataset_w_attrs(client, PROJECT, DS_ID):
109113
"friendlyName": FRIENDLY_NAME,
110114
"location": LOCATION,
111115
"defaultTableExpirationMs": "3600",
116+
"defaultRoundingMode": DEFAULT_ROUNDING_MODE,
112117
"access": [
113118
{"role": "OWNER", "userByEmail": USER_EMAIL},
114119
{"view": VIEW, "role": None},
@@ -365,3 +370,106 @@ def test_create_dataset_alreadyexists_w_exists_ok_true(PROJECT, DS_ID, LOCATION)
365370
mock.call(method="GET", path=get_path, timeout=DEFAULT_TIMEOUT),
366371
]
367372
)
373+
374+
375+
def test_create_dataset_with_default_rounding_mode_if_value_is_none(
376+
PROJECT, DS_ID, LOCATION
377+
):
378+
default_rounding_mode = None
379+
path = "/projects/%s/datasets" % PROJECT
380+
resource = {
381+
"datasetReference": {"projectId": PROJECT, "datasetId": DS_ID},
382+
"etag": "etag",
383+
"id": "{}:{}".format(PROJECT, DS_ID),
384+
"location": LOCATION,
385+
}
386+
client = make_client(location=LOCATION)
387+
conn = client._connection = make_connection(resource)
388+
389+
ds_ref = DatasetReference(PROJECT, DS_ID)
390+
before = Dataset(ds_ref)
391+
before.default_rounding_mode = default_rounding_mode
392+
after = client.create_dataset(before)
393+
394+
print(40 * "-")
395+
print(after)
396+
print(40 * "-")
397+
assert after.dataset_id == DS_ID
398+
assert after.project == PROJECT
399+
assert after.default_rounding_mode is None
400+
401+
conn.api_request.assert_called_once_with(
402+
method="POST",
403+
path=path,
404+
data={
405+
"datasetReference": {"projectId": PROJECT, "datasetId": DS_ID},
406+
"labels": {},
407+
"location": LOCATION,
408+
"defaultRoundingMode": "ROUNDING_MODE_UNSPECIFIED",
409+
},
410+
timeout=DEFAULT_TIMEOUT,
411+
)
412+
413+
414+
def test_create_dataset_with_default_rounding_mode_if_value_is_not_string(
415+
PROJECT, DS_ID, LOCATION
416+
):
417+
default_rounding_mode = 10
418+
ds_ref = DatasetReference(PROJECT, DS_ID)
419+
dataset = Dataset(ds_ref)
420+
with pytest.raises(ValueError) as e:
421+
dataset.default_rounding_mode = default_rounding_mode
422+
assert str(e.value) == "Pass a string, or None"
423+
424+
425+
def test_create_dataset_with_default_rounding_mode_if_value_is_not_valid(
426+
PROJECT, DS_ID
427+
):
428+
default_rounding_mode = "ROUND_HALF_AWAY_FROM_ZEROS"
429+
ds_ref = DatasetReference(PROJECT, DS_ID)
430+
dataset = Dataset(ds_ref)
431+
with pytest.raises(ValueError) as e:
432+
dataset.default_rounding_mode = default_rounding_mode
433+
assert (
434+
str(e.value)
435+
== "rounding mode needs to be one of ROUNDING_MODE_UNSPECIFIED,ROUND_HALF_AWAY_FROM_ZERO,ROUND_HALF_EVEN"
436+
)
437+
438+
439+
def test_create_dataset_with_default_rounding_mode_if_value_is_valid(
440+
PROJECT, DS_ID, LOCATION
441+
):
442+
default_rounding_mode = "ROUND_HALF_AWAY_FROM_ZERO"
443+
path = "/projects/%s/datasets" % PROJECT
444+
resource = {
445+
"datasetReference": {"projectId": PROJECT, "datasetId": DS_ID},
446+
"etag": "etag",
447+
"id": "{}:{}".format(PROJECT, DS_ID),
448+
"location": LOCATION,
449+
}
450+
client = make_client(location=LOCATION)
451+
conn = client._connection = make_connection(resource)
452+
453+
ds_ref = DatasetReference(PROJECT, DS_ID)
454+
before = Dataset(ds_ref)
455+
before.default_rounding_mode = default_rounding_mode
456+
after = client.create_dataset(before)
457+
458+
print(40 * "-")
459+
print(after)
460+
print(40 * "-")
461+
assert after.dataset_id == DS_ID
462+
assert after.project == PROJECT
463+
assert after.default_rounding_mode is None
464+
465+
conn.api_request.assert_called_once_with(
466+
method="POST",
467+
path=path,
468+
data={
469+
"datasetReference": {"projectId": PROJECT, "datasetId": DS_ID},
470+
"labels": {},
471+
"location": LOCATION,
472+
"defaultRoundingMode": default_rounding_mode,
473+
},
474+
timeout=DEFAULT_TIMEOUT,
475+
)

0 commit comments

Comments
 (0)