Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/cloud/bigquery/model.py: 49%
175 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:07 +0000
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:07 +0000
1# -*- coding: utf-8 -*-
2#
3# Copyright 2019 Google LLC
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# https://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
17"""Define resources for the BigQuery ML Models API."""
19import copy
20import datetime
21import typing
22from typing import Any, Dict, Optional, Sequence, Union
24import google.cloud._helpers # type: ignore
25from google.cloud.bigquery import _helpers
26from google.cloud.bigquery import standard_sql
27from google.cloud.bigquery.encryption_configuration import EncryptionConfiguration
30class Model:
31 """Model represents a machine learning model resource.
33 See
34 https://cloud.google.com/bigquery/docs/reference/rest/v2/models
36 Args:
37 model_ref:
38 A pointer to a model. If ``model_ref`` is a string, it must
39 included a project ID, dataset ID, and model ID, each separated
40 by ``.``.
41 """
43 _PROPERTY_TO_API_FIELD = {
44 "expires": "expirationTime",
45 "friendly_name": "friendlyName",
46 # Even though it's not necessary for field mapping to map when the
47 # property name equals the resource name, we add these here so that we
48 # have an exhaustive list of all mutable properties.
49 "labels": "labels",
50 "description": "description",
51 "encryption_configuration": "encryptionConfiguration",
52 }
54 def __init__(self, model_ref: Union["ModelReference", str, None]):
55 # Use _properties on read-write properties to match the REST API
56 # semantics. The BigQuery API makes a distinction between an unset
57 # value, a null value, and a default value (0 or ""), but the protocol
58 # buffer classes do not.
59 self._properties = {}
61 if isinstance(model_ref, str):
62 model_ref = ModelReference.from_string(model_ref)
64 if model_ref:
65 self._properties["modelReference"] = model_ref.to_api_repr()
67 @property
68 def reference(self) -> Optional["ModelReference"]:
69 """A model reference pointing to this model.
71 Read-only.
72 """
73 resource = self._properties.get("modelReference")
74 if resource is None:
75 return None
76 else:
77 return ModelReference.from_api_repr(resource)
79 @property
80 def project(self) -> Optional[str]:
81 """Project bound to the model."""
82 ref = self.reference
83 return ref.project if ref is not None else None
85 @property
86 def dataset_id(self) -> Optional[str]:
87 """ID of dataset containing the model."""
88 ref = self.reference
89 return ref.dataset_id if ref is not None else None
91 @property
92 def model_id(self) -> Optional[str]:
93 """The model ID."""
94 ref = self.reference
95 return ref.model_id if ref is not None else None
97 @property
98 def path(self) -> Optional[str]:
99 """URL path for the model's APIs."""
100 ref = self.reference
101 return ref.path if ref is not None else None
103 @property
104 def location(self) -> Optional[str]:
105 """The geographic location where the model resides.
107 This value is inherited from the dataset.
109 Read-only.
110 """
111 return typing.cast(Optional[str], self._properties.get("location"))
113 @property
114 def etag(self) -> Optional[str]:
115 """ETag for the model resource (:data:`None` until set from the server).
117 Read-only.
118 """
119 return typing.cast(Optional[str], self._properties.get("etag"))
121 @property
122 def created(self) -> Optional[datetime.datetime]:
123 """Datetime at which the model was created (:data:`None` until set from the server).
125 Read-only.
126 """
127 value = typing.cast(Optional[float], self._properties.get("creationTime"))
128 if value is None:
129 return None
130 else:
131 # value will be in milliseconds.
132 return google.cloud._helpers._datetime_from_microseconds(
133 1000.0 * float(value)
134 )
136 @property
137 def modified(self) -> Optional[datetime.datetime]:
138 """Datetime at which the model was last modified (:data:`None` until set from the server).
140 Read-only.
141 """
142 value = typing.cast(Optional[float], self._properties.get("lastModifiedTime"))
143 if value is None:
144 return None
145 else:
146 # value will be in milliseconds.
147 return google.cloud._helpers._datetime_from_microseconds(
148 1000.0 * float(value)
149 )
151 @property
152 def model_type(self) -> str:
153 """Type of the model resource.
155 Read-only.
156 """
157 return typing.cast(
158 str, self._properties.get("modelType", "MODEL_TYPE_UNSPECIFIED")
159 )
161 @property
162 def training_runs(self) -> Sequence[Dict[str, Any]]:
163 """Information for all training runs in increasing order of start time.
165 Dictionaries are in REST API format. See:
166 https://cloud.google.com/bigquery/docs/reference/rest/v2/models#trainingrun
168 Read-only.
169 """
170 return typing.cast(
171 Sequence[Dict[str, Any]], self._properties.get("trainingRuns", [])
172 )
174 @property
175 def feature_columns(self) -> Sequence[standard_sql.StandardSqlField]:
176 """Input feature columns that were used to train this model.
178 Read-only.
179 """
180 resource: Sequence[Dict[str, Any]] = typing.cast(
181 Sequence[Dict[str, Any]], self._properties.get("featureColumns", [])
182 )
183 return [
184 standard_sql.StandardSqlField.from_api_repr(column) for column in resource
185 ]
187 @property
188 def label_columns(self) -> Sequence[standard_sql.StandardSqlField]:
189 """Label columns that were used to train this model.
191 The output of the model will have a ``predicted_`` prefix to these columns.
193 Read-only.
194 """
195 resource: Sequence[Dict[str, Any]] = typing.cast(
196 Sequence[Dict[str, Any]], self._properties.get("labelColumns", [])
197 )
198 return [
199 standard_sql.StandardSqlField.from_api_repr(column) for column in resource
200 ]
202 @property
203 def best_trial_id(self) -> Optional[int]:
204 """The best trial_id across all training runs.
206 .. deprecated::
207 This property is deprecated!
209 Read-only.
210 """
211 value = typing.cast(Optional[int], self._properties.get("bestTrialId"))
212 if value is not None:
213 value = int(value)
214 return value
216 @property
217 def expires(self) -> Optional[datetime.datetime]:
218 """The datetime when this model expires.
220 If not present, the model will persist indefinitely. Expired models will be
221 deleted and their storage reclaimed.
222 """
223 value = typing.cast(Optional[float], self._properties.get("expirationTime"))
224 if value is None:
225 return None
226 else:
227 # value will be in milliseconds.
228 return google.cloud._helpers._datetime_from_microseconds(
229 1000.0 * float(value)
230 )
232 @expires.setter
233 def expires(self, value: Optional[datetime.datetime]):
234 if value is None:
235 value_to_store: Optional[str] = None
236 else:
237 value_to_store = str(google.cloud._helpers._millis_from_datetime(value))
238 # TODO: Consider using typing.TypedDict when only Python 3.8+ is supported.
239 self._properties["expirationTime"] = value_to_store # type: ignore
241 @property
242 def description(self) -> Optional[str]:
243 """Description of the model (defaults to :data:`None`)."""
244 return typing.cast(Optional[str], self._properties.get("description"))
246 @description.setter
247 def description(self, value: Optional[str]):
248 # TODO: Consider using typing.TypedDict when only Python 3.8+ is supported.
249 self._properties["description"] = value # type: ignore
251 @property
252 def friendly_name(self) -> Optional[str]:
253 """Title of the table (defaults to :data:`None`)."""
254 return typing.cast(Optional[str], self._properties.get("friendlyName"))
256 @friendly_name.setter
257 def friendly_name(self, value: Optional[str]):
258 # TODO: Consider using typing.TypedDict when only Python 3.8+ is supported.
259 self._properties["friendlyName"] = value # type: ignore
261 @property
262 def labels(self) -> Dict[str, str]:
263 """Labels for the table.
265 This method always returns a dict. To change a model's labels, modify the dict,
266 then call ``Client.update_model``. To delete a label, set its value to
267 :data:`None` before updating.
268 """
269 return self._properties.setdefault("labels", {})
271 @labels.setter
272 def labels(self, value: Optional[Dict[str, str]]):
273 if value is None:
274 value = {}
275 self._properties["labels"] = value
277 @property
278 def encryption_configuration(self) -> Optional[EncryptionConfiguration]:
279 """Custom encryption configuration for the model.
281 Custom encryption configuration (e.g., Cloud KMS keys) or :data:`None`
282 if using default encryption.
284 See `protecting data with Cloud KMS keys
285 <https://cloud.google.com/bigquery/docs/customer-managed-encryption>`_
286 in the BigQuery documentation.
287 """
288 prop = self._properties.get("encryptionConfiguration")
289 if prop:
290 prop = EncryptionConfiguration.from_api_repr(prop)
291 return typing.cast(Optional[EncryptionConfiguration], prop)
293 @encryption_configuration.setter
294 def encryption_configuration(self, value: Optional[EncryptionConfiguration]):
295 api_repr = value.to_api_repr() if value else value
296 self._properties["encryptionConfiguration"] = api_repr
298 @classmethod
299 def from_api_repr(cls, resource: Dict[str, Any]) -> "Model":
300 """Factory: construct a model resource given its API representation
302 Args:
303 resource:
304 Model resource representation from the API
306 Returns:
307 Model parsed from ``resource``.
308 """
309 this = cls(None)
310 resource = copy.deepcopy(resource)
311 this._properties = resource
312 return this
314 def _build_resource(self, filter_fields):
315 """Generate a resource for ``update``."""
316 return _helpers._build_resource_from_properties(self, filter_fields)
318 def __repr__(self):
319 return f"Model(reference={self.reference!r})"
321 def to_api_repr(self) -> Dict[str, Any]:
322 """Construct the API resource representation of this model.
324 Returns:
325 Model reference represented as an API resource
326 """
327 return copy.deepcopy(self._properties)
330class ModelReference:
331 """ModelReferences are pointers to models.
333 See
334 https://cloud.google.com/bigquery/docs/reference/rest/v2/models#modelreference
335 """
337 def __init__(self):
338 self._properties = {}
340 @property
341 def project(self):
342 """str: Project bound to the model"""
343 return self._properties.get("projectId")
345 @property
346 def dataset_id(self):
347 """str: ID of dataset containing the model."""
348 return self._properties.get("datasetId")
350 @property
351 def model_id(self):
352 """str: The model ID."""
353 return self._properties.get("modelId")
355 @property
356 def path(self) -> str:
357 """URL path for the model's APIs."""
358 return f"/projects/{self.project}/datasets/{self.dataset_id}/models/{self.model_id}"
360 @classmethod
361 def from_api_repr(cls, resource: Dict[str, Any]) -> "ModelReference":
362 """Factory: construct a model reference given its API representation.
364 Args:
365 resource:
366 Model reference representation returned from the API
368 Returns:
369 Model reference parsed from ``resource``.
370 """
371 ref = cls()
372 ref._properties = resource
373 return ref
375 @classmethod
376 def from_string(
377 cls, model_id: str, default_project: Optional[str] = None
378 ) -> "ModelReference":
379 """Construct a model reference from model ID string.
381 Args:
382 model_id:
383 A model ID in standard SQL format. If ``default_project``
384 is not specified, this must included a project ID, dataset
385 ID, and model ID, each separated by ``.``.
386 default_project:
387 The project ID to use when ``model_id`` does not include
388 a project ID.
390 Returns:
391 Model reference parsed from ``model_id``.
393 Raises:
394 ValueError:
395 If ``model_id`` is not a fully-qualified table ID in
396 standard SQL format.
397 """
398 proj, dset, model = _helpers._parse_3_part_id(
399 model_id, default_project=default_project, property_name="model_id"
400 )
401 return cls.from_api_repr(
402 {"projectId": proj, "datasetId": dset, "modelId": model}
403 )
405 def to_api_repr(self) -> Dict[str, Any]:
406 """Construct the API resource representation of this model reference.
408 Returns:
409 Model reference represented as an API resource.
410 """
411 return copy.deepcopy(self._properties)
413 def _key(self):
414 """Unique key for this model.
416 This is used for hashing a ModelReference.
417 """
418 return self.project, self.dataset_id, self.model_id
420 def __eq__(self, other):
421 if not isinstance(other, ModelReference):
422 return NotImplemented
423 return self._properties == other._properties
425 def __ne__(self, other):
426 return not self == other
428 def __hash__(self):
429 return hash(self._key())
431 def __repr__(self):
432 return "ModelReference(project_id='{}', dataset_id='{}', model_id='{}')".format(
433 self.project, self.dataset_id, self.model_id
434 )
437def _model_arg_to_model_ref(value, default_project=None):
438 """Helper to convert a string or Model to ModelReference.
440 This function keeps ModelReference and other kinds of objects unchanged.
441 """
442 if isinstance(value, str):
443 return ModelReference.from_string(value, default_project=default_project)
444 if isinstance(value, Model):
445 return value.reference
446 return value