tilebox-datasets 0.44.0__py3-none-any.whl → 0.46.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,7 @@ from _tilebox.grpc.aio.error import with_pythonic_errors
5
5
  from tilebox.datasets.aio.dataset import DatasetClient
6
6
  from tilebox.datasets.client import Client as BaseClient
7
7
  from tilebox.datasets.client import token_from_env
8
+ from tilebox.datasets.data.datasets import DatasetKind, FieldDict
8
9
  from tilebox.datasets.datasets.v1.collections_pb2_grpc import CollectionServiceStub
9
10
  from tilebox.datasets.datasets.v1.data_access_pb2_grpc import DataAccessServiceStub
10
11
  from tilebox.datasets.datasets.v1.data_ingestion_pb2_grpc import DataIngestionServiceStub
@@ -32,10 +33,47 @@ class Client:
32
33
  )
33
34
  self._client = BaseClient(service)
34
35
 
36
+ async def create_dataset(
37
+ self,
38
+ kind: DatasetKind,
39
+ code_name: str,
40
+ fields: list[FieldDict],
41
+ *,
42
+ name: str | None = None,
43
+ description: str | None = None,
44
+ ) -> DatasetClient:
45
+ """Create a new dataset.
46
+
47
+ Args:
48
+ kind: The kind of the dataset.
49
+ code_name: The code name of the dataset.
50
+ fields: The fields of the dataset.
51
+ name: The name of the dataset. Defaults to the code name.
52
+ description: A short description of the dataset. Optional.
53
+
54
+ Returns:
55
+ The created dataset.
56
+ """
57
+ if name is None:
58
+ name = code_name
59
+ if description is None:
60
+ description = ""
61
+
62
+ return await self._client.create_dataset(kind, code_name, fields, name, description, DatasetClient)
63
+
35
64
  async def datasets(self) -> Group:
65
+ """Fetch all available datasets."""
36
66
  return await self._client.datasets(DatasetClient)
37
67
 
38
68
  async def dataset(self, slug: str) -> DatasetClient:
69
+ """Get a dataset by its slug, e.g. `open_data.copernicus.sentinel1_sar`.
70
+
71
+ Args:
72
+ slug: The slug of the dataset.
73
+
74
+ Returns:
75
+ The dataset if it exists.
76
+ """
39
77
  return await self._client.dataset(slug, DatasetClient)
40
78
 
41
79
  async def _dataset_by_id(self, dataset_id: str | UUID) -> DatasetClient:
@@ -7,7 +7,7 @@ from loguru import logger
7
7
  from promise import Promise
8
8
 
9
9
  from _tilebox.grpc.channel import parse_channel_info
10
- from tilebox.datasets.data.datasets import Dataset, DatasetGroup, ListDatasetsResponse
10
+ from tilebox.datasets.data.datasets import Dataset, DatasetGroup, DatasetKind, FieldDict, ListDatasetsResponse
11
11
  from tilebox.datasets.group import Group
12
12
  from tilebox.datasets.message_pool import register_once
13
13
  from tilebox.datasets.service import TileboxDatasetService
@@ -26,8 +26,16 @@ class Client:
26
26
  def __init__(self, service: TileboxDatasetService) -> None:
27
27
  self._service = service
28
28
 
29
+ def create_dataset( # noqa: PLR0913
30
+ self, kind: DatasetKind, code_name: str, fields: list[FieldDict], name: str, summary: str, dataset_type: type[T]
31
+ ) -> Promise[T]:
32
+ return (
33
+ self._service.create_dataset(kind, code_name, fields, name, summary)
34
+ .then(_ensure_registered)
35
+ .then(lambda dataset: dataset_type(self._service, dataset))
36
+ )
37
+
29
38
  def datasets(self, dataset_type: type[T]) -> Promise[Group]:
30
- """Fetch all available datasets."""
31
39
  return (
32
40
  self._service.list_datasets()
33
41
  .then(_log_server_message)
@@ -40,16 +48,6 @@ class Client:
40
48
  )
41
49
 
42
50
  def dataset(self, slug: str, dataset_type: type[T]) -> Promise[T]:
43
- """
44
- Get a dataset by its slug, e.g. `open_data.copernicus.sentinel1_sar`.
45
-
46
- Args:
47
- slug: The slug of the dataset
48
-
49
- Returns:
50
- The dataset if it exists.
51
- """
52
-
53
51
  return (
54
52
  self._service.get_dataset_by_slug(slug)
55
53
  .then(_ensure_registered)
@@ -1,12 +1,29 @@
1
1
  from dataclasses import dataclass
2
+ from datetime import datetime, timedelta
3
+ from enum import Enum
4
+ from typing import TypedDict, get_args, get_origin
2
5
  from uuid import UUID
3
6
 
4
- from google.protobuf.descriptor_pb2 import FileDescriptorSet
7
+ import numpy as np
8
+ from google.protobuf import duration_pb2, timestamp_pb2
9
+ from google.protobuf.descriptor_pb2 import FieldDescriptorProto, FileDescriptorSet
10
+ from shapely import Geometry
11
+ from typing_extensions import NotRequired, Required
5
12
 
6
- from tilebox.datasets.datasets.v1 import core_pb2, dataset_type_pb2, datasets_pb2
13
+ from tilebox.datasets.datasets.v1 import core_pb2, dataset_type_pb2, datasets_pb2, well_known_types_pb2
7
14
  from tilebox.datasets.uuid import uuid_message_to_optional_uuid, uuid_message_to_uuid, uuid_to_uuid_message
8
15
 
9
16
 
17
+ class DatasetKind(Enum):
18
+ TEMPORAL = dataset_type_pb2.DATASET_KIND_TEMPORAL
19
+ """A dataset that contains a timestamp field."""
20
+ SPATIOTEMPORAL = dataset_type_pb2.DATASET_KIND_SPATIOTEMPORAL
21
+ """A dataset that contains a timestamp field and a geometry field."""
22
+
23
+
24
+ _dataset_kind_int_to_enum = {kind.value: kind for kind in DatasetKind}
25
+
26
+
10
27
  @dataclass(frozen=True)
11
28
  class FieldAnnotation:
12
29
  description: str
@@ -20,6 +37,116 @@ class FieldAnnotation:
20
37
  return dataset_type_pb2.FieldAnnotation(description=self.description, example_value=self.example_value)
21
38
 
22
39
 
40
+ class FieldDict(TypedDict):
41
+ name: Required[str]
42
+ type: Required[
43
+ type[str]
44
+ | type[list[str]]
45
+ | type[bytes]
46
+ | type[list[bytes]]
47
+ | type[bool]
48
+ | type[list[bool]]
49
+ | type[int]
50
+ | type[list[int]]
51
+ | type[np.uint64]
52
+ | type[list[np.uint64]]
53
+ | type[float]
54
+ | type[list[float]]
55
+ | type[timedelta]
56
+ | type[list[timedelta]]
57
+ | type[datetime]
58
+ | type[list[datetime]]
59
+ | type[UUID]
60
+ | type[list[UUID]]
61
+ | type[Geometry]
62
+ | type[list[Geometry]]
63
+ ]
64
+ description: NotRequired[str]
65
+ example_value: NotRequired[str]
66
+
67
+
68
+ _TYPE_INFO: dict[type, tuple[FieldDescriptorProto.Type.ValueType, str | None]] = {
69
+ str: (FieldDescriptorProto.TYPE_STRING, None),
70
+ bytes: (FieldDescriptorProto.TYPE_BYTES, None),
71
+ bool: (FieldDescriptorProto.TYPE_BOOL, None),
72
+ int: (FieldDescriptorProto.TYPE_INT64, None),
73
+ np.uint64: (FieldDescriptorProto.TYPE_UINT64, None),
74
+ float: (FieldDescriptorProto.TYPE_DOUBLE, None),
75
+ timedelta: (FieldDescriptorProto.TYPE_MESSAGE, f".{duration_pb2.Duration.DESCRIPTOR.full_name}"),
76
+ datetime: (FieldDescriptorProto.TYPE_MESSAGE, f".{timestamp_pb2.Timestamp.DESCRIPTOR.full_name}"),
77
+ UUID: (FieldDescriptorProto.TYPE_MESSAGE, f".{well_known_types_pb2.UUID.DESCRIPTOR.full_name}"),
78
+ Geometry: (FieldDescriptorProto.TYPE_MESSAGE, f".{well_known_types_pb2.Geometry.DESCRIPTOR.full_name}"),
79
+ }
80
+
81
+
82
+ @dataclass(frozen=True)
83
+ class Field:
84
+ descriptor: FieldDescriptorProto
85
+ annotation: FieldAnnotation
86
+ queryable: bool
87
+
88
+ @classmethod
89
+ def from_message(cls, field: dataset_type_pb2.Field) -> "Field":
90
+ return cls(
91
+ descriptor=field.descriptor,
92
+ annotation=FieldAnnotation.from_message(field.annotation),
93
+ queryable=field.queryable,
94
+ )
95
+
96
+ @classmethod
97
+ def from_dict(cls, field: FieldDict) -> "Field":
98
+ origin = get_origin(field["type"])
99
+ if origin is list:
100
+ label = FieldDescriptorProto.Label.LABEL_REPEATED
101
+ args = get_args(field["type"])
102
+ inner_type = args[0] if args else field["type"]
103
+ else:
104
+ label = FieldDescriptorProto.Label.LABEL_OPTIONAL
105
+ inner_type = field["type"]
106
+
107
+ (field_type, field_type_name) = _TYPE_INFO[inner_type]
108
+
109
+ return cls(
110
+ descriptor=FieldDescriptorProto(
111
+ name=field["name"],
112
+ type=field_type,
113
+ type_name=field_type_name,
114
+ label=label,
115
+ ),
116
+ annotation=FieldAnnotation(
117
+ description=field.get("description", ""),
118
+ example_value=field.get("example_value", ""),
119
+ ),
120
+ queryable=False,
121
+ )
122
+
123
+ def to_message(self) -> dataset_type_pb2.Field:
124
+ return dataset_type_pb2.Field(
125
+ descriptor=self.descriptor,
126
+ annotation=self.annotation.to_message(),
127
+ queryable=self.queryable,
128
+ )
129
+
130
+
131
+ @dataclass(frozen=True)
132
+ class DatasetType:
133
+ kind: DatasetKind | None
134
+ fields: list[Field]
135
+
136
+ @classmethod
137
+ def from_message(cls, dataset_type: dataset_type_pb2.DatasetType) -> "DatasetType":
138
+ return cls(
139
+ kind=_dataset_kind_int_to_enum.get(dataset_type.kind, None),
140
+ fields=[Field.from_message(f) for f in dataset_type.fields],
141
+ )
142
+
143
+ def to_message(self) -> dataset_type_pb2.DatasetType:
144
+ return dataset_type_pb2.DatasetType(
145
+ kind=self.kind.value if self.kind else dataset_type_pb2.DATASET_KIND_UNSPECIFIED,
146
+ fields=[f.to_message() for f in self.fields],
147
+ )
148
+
149
+
23
150
  @dataclass(frozen=True)
24
151
  class AnnotatedType:
25
152
  descriptor_set: FileDescriptorSet
@@ -28,7 +28,7 @@ from tilebox.datasets.tilebox.v1 import id_pb2 as tilebox_dot_v1_dot_id__pb2
28
28
  from tilebox.datasets.tilebox.v1 import query_pb2 as tilebox_dot_v1_dot_query__pb2
29
29
 
30
30
 
31
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x64\x61tasets/v1/core.proto\x12\x0b\x64\x61tasets.v1\x1a\x1e\x64\x61tasets/v1/dataset_type.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x13tilebox/v1/id.proto\x1a\x16tilebox/v1/query.proto\"]\n\x10LegacyPagination\x12\x1b\n\x05limit\x18\x01 \x01(\x03\x42\x05\xaa\x01\x02\x08\x01R\x05limit\x12,\n\x0estarting_after\x18\x02 \x01(\tB\x05\xaa\x01\x02\x08\x01R\rstartingAfter\"6\n\x03\x41ny\x12\x19\n\x08type_url\x18\x01 \x01(\tR\x07typeUrl\x12\x14\n\x05value\x18\x02 \x01(\x0cR\x05value\">\n\x0bRepeatedAny\x12\x19\n\x08type_url\x18\x01 \x01(\tR\x07typeUrl\x12\x14\n\x05value\x18\x02 \x03(\x0cR\x05value\"\xa8\x01\n\x11\x44\x61tapointMetadata\x12\x39\n\nevent_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\teventTime\x12\x41\n\x0eingestion_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\ringestionTime\x12\x15\n\x02id\x18\x03 \x01(\tB\x05\xaa\x01\x02\x08\x01R\x02id\"\xb4\x01\n\rDatapointPage\x12\x32\n\x04meta\x18\x01 \x03(\x0b\x32\x1e.datasets.v1.DatapointMetadataR\x04meta\x12,\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x18.datasets.v1.RepeatedAnyR\x04\x64\x61ta\x12\x41\n\tnext_page\x18\x03 \x01(\x0b\x32\x1d.datasets.v1.LegacyPaginationB\x05\xaa\x01\x02\x08\x01R\x08nextPage\"e\n\tDatapoint\x12\x32\n\x04meta\x18\x01 \x01(\x0b\x32\x1e.datasets.v1.DatapointMetadataR\x04meta\x12$\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x10.datasets.v1.AnyR\x04\x64\x61ta\"]\n\nCollection\x12\x1b\n\tlegacy_id\x18\x01 \x01(\tR\x08legacyId\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x1e\n\x02id\x18\x03 \x01(\x0b\x32\x0e.tilebox.v1.IDR\x02id\"\xab\x01\n\x0e\x43ollectionInfo\x12\x37\n\ncollection\x18\x01 \x01(\x0b\x32\x17.datasets.v1.CollectionR\ncollection\x12\x43\n\x0c\x61vailability\x18\x02 \x01(\x0b\x32\x18.tilebox.v1.TimeIntervalB\x05\xaa\x01\x02\x08\x01R\x0c\x61vailability\x12\x1b\n\x05\x63ount\x18\x03 \x01(\x04\x42\x05\xaa\x01\x02\x08\x01R\x05\x63ount\"B\n\x0f\x43ollectionInfos\x12/\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\x1b.datasets.v1.CollectionInfoR\x04\x64\x61ta\"\xb9\x03\n\x07\x44\x61taset\x12\x1e\n\x02id\x18\x01 \x01(\x0b\x32\x0e.tilebox.v1.IDR\x02id\x12)\n\x08group_id\x18\x02 \x01(\x0b\x32\x0e.tilebox.v1.IDR\x07groupId\x12.\n\x04type\x18\x03 \x01(\x0b\x32\x1a.datasets.v1.AnnotatedTypeR\x04type\x12\x1b\n\tcode_name\x18\x04 \x01(\tR\x08\x63odeName\x12\x12\n\x04name\x18\x05 \x01(\tR\x04name\x12\x18\n\x07summary\x18\x06 \x01(\tR\x07summary\x12\x12\n\x04icon\x18\x07 \x01(\tR\x04icon\x12 \n\x0b\x64\x65scription\x18\x08 \x01(\tR\x0b\x64\x65scription\x12@\n\x0bpermissions\x18\n \x03(\x0e\x32\x1e.datasets.v1.DatasetPermissionR\x0bpermissions\x12\x37\n\nvisibility\x18\x0b \x01(\x0e\x32\x17.datasets.v1.VisibilityR\nvisibility\x12\x12\n\x04slug\x18\x0c \x01(\tR\x04slug\x12#\n\rtype_editable\x18\r \x01(\x08R\x0ctypeEditable\"\xa0\x01\n\x0c\x44\x61tasetGroup\x12\x1e\n\x02id\x18\x01 \x01(\x0b\x32\x0e.tilebox.v1.IDR\x02id\x12+\n\tparent_id\x18\x02 \x01(\x0b\x32\x0e.tilebox.v1.IDR\x08parentId\x12\x1b\n\tcode_name\x18\x03 \x01(\tR\x08\x63odeName\x12\x12\n\x04name\x18\x04 \x01(\tR\x04name\x12\x12\n\x04icon\x18\x05 \x01(\tR\x04icon*\x9b\x01\n\x11\x44\x61tasetPermission\x12\"\n\x1e\x44\x41TASET_PERMISSION_UNSPECIFIED\x10\x00\x12\"\n\x1e\x44\x41TASET_PERMISSION_ACCESS_DATA\x10\x01\x12!\n\x1d\x44\x41TASET_PERMISSION_WRITE_DATA\x10\x02\x12\x1b\n\x17\x44\x41TASET_PERMISSION_EDIT\x10\x03*v\n\nVisibility\x12\x1a\n\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x16\n\x12VISIBILITY_PRIVATE\x10\x01\x12\x1d\n\x19VISIBILITY_SHARED_WITH_ME\x10\x02\x12\x15\n\x11VISIBILITY_PUBLIC\x10\x03\x42n\n\x0f\x63om.datasets.v1B\tCoreProtoP\x01\xa2\x02\x03\x44XX\xaa\x02\x0b\x44\x61tasets.V1\xca\x02\x0b\x44\x61tasets\\V1\xe2\x02\x17\x44\x61tasets\\V1\\GPBMetadata\xea\x02\x0c\x44\x61tasets::V1\x92\x03\x02\x08\x02\x62\x08\x65\x64itionsp\xe8\x07')
31
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x64\x61tasets/v1/core.proto\x12\x0b\x64\x61tasets.v1\x1a\x1e\x64\x61tasets/v1/dataset_type.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x13tilebox/v1/id.proto\x1a\x16tilebox/v1/query.proto\"]\n\x10LegacyPagination\x12\x1b\n\x05limit\x18\x01 \x01(\x03\x42\x05\xaa\x01\x02\x08\x01R\x05limit\x12,\n\x0estarting_after\x18\x02 \x01(\tB\x05\xaa\x01\x02\x08\x01R\rstartingAfter\"6\n\x03\x41ny\x12\x19\n\x08type_url\x18\x01 \x01(\tR\x07typeUrl\x12\x14\n\x05value\x18\x02 \x01(\x0cR\x05value\">\n\x0bRepeatedAny\x12\x19\n\x08type_url\x18\x01 \x01(\tR\x07typeUrl\x12\x14\n\x05value\x18\x02 \x03(\x0cR\x05value\"\xa8\x01\n\x11\x44\x61tapointMetadata\x12\x39\n\nevent_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\teventTime\x12\x41\n\x0eingestion_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\ringestionTime\x12\x15\n\x02id\x18\x03 \x01(\tB\x05\xaa\x01\x02\x08\x01R\x02id\"\xb4\x01\n\rDatapointPage\x12\x32\n\x04meta\x18\x01 \x03(\x0b\x32\x1e.datasets.v1.DatapointMetadataR\x04meta\x12,\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x18.datasets.v1.RepeatedAnyR\x04\x64\x61ta\x12\x41\n\tnext_page\x18\x03 \x01(\x0b\x32\x1d.datasets.v1.LegacyPaginationB\x05\xaa\x01\x02\x08\x01R\x08nextPage\"e\n\tDatapoint\x12\x32\n\x04meta\x18\x01 \x01(\x0b\x32\x1e.datasets.v1.DatapointMetadataR\x04meta\x12$\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x10.datasets.v1.AnyR\x04\x64\x61ta\"]\n\nCollection\x12\x1b\n\tlegacy_id\x18\x01 \x01(\tR\x08legacyId\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x1e\n\x02id\x18\x03 \x01(\x0b\x32\x0e.tilebox.v1.IDR\x02id\"\xab\x01\n\x0e\x43ollectionInfo\x12\x37\n\ncollection\x18\x01 \x01(\x0b\x32\x17.datasets.v1.CollectionR\ncollection\x12\x43\n\x0c\x61vailability\x18\x02 \x01(\x0b\x32\x18.tilebox.v1.TimeIntervalB\x05\xaa\x01\x02\x08\x01R\x0c\x61vailability\x12\x1b\n\x05\x63ount\x18\x03 \x01(\x04\x42\x05\xaa\x01\x02\x08\x01R\x05\x63ount\"B\n\x0f\x43ollectionInfos\x12/\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\x1b.datasets.v1.CollectionInfoR\x04\x64\x61ta\"\xf8\x03\n\x07\x44\x61taset\x12\x1e\n\x02id\x18\x01 \x01(\x0b\x32\x0e.tilebox.v1.IDR\x02id\x12)\n\x08group_id\x18\x02 \x01(\x0b\x32\x0e.tilebox.v1.IDR\x07groupId\x12.\n\x04type\x18\x03 \x01(\x0b\x32\x1a.datasets.v1.AnnotatedTypeR\x04type\x12\x1b\n\tcode_name\x18\x04 \x01(\tR\x08\x63odeName\x12\x12\n\x04name\x18\x05 \x01(\tR\x04name\x12\x18\n\x07summary\x18\x06 \x01(\tR\x07summary\x12\x12\n\x04icon\x18\x07 \x01(\tR\x04icon\x12 \n\x0b\x64\x65scription\x18\x08 \x01(\tR\x0b\x64\x65scription\x12@\n\x0bpermissions\x18\n \x03(\x0e\x32\x1e.datasets.v1.DatasetPermissionR\x0bpermissions\x12\x37\n\nvisibility\x18\x0b \x01(\x0e\x32\x17.datasets.v1.VisibilityR\nvisibility\x12\x12\n\x04slug\x18\x0c \x01(\tR\x04slug\x12#\n\rtype_editable\x18\r \x01(\x08R\x0ctypeEditable\x12=\n\x0b\x63ollections\x18\x0e \x03(\x0b\x32\x1b.datasets.v1.CollectionInfoR\x0b\x63ollections\"\xa0\x01\n\x0c\x44\x61tasetGroup\x12\x1e\n\x02id\x18\x01 \x01(\x0b\x32\x0e.tilebox.v1.IDR\x02id\x12+\n\tparent_id\x18\x02 \x01(\x0b\x32\x0e.tilebox.v1.IDR\x08parentId\x12\x1b\n\tcode_name\x18\x03 \x01(\tR\x08\x63odeName\x12\x12\n\x04name\x18\x04 \x01(\tR\x04name\x12\x12\n\x04icon\x18\x05 \x01(\tR\x04icon*\x9b\x01\n\x11\x44\x61tasetPermission\x12\"\n\x1e\x44\x41TASET_PERMISSION_UNSPECIFIED\x10\x00\x12\"\n\x1e\x44\x41TASET_PERMISSION_ACCESS_DATA\x10\x01\x12!\n\x1d\x44\x41TASET_PERMISSION_WRITE_DATA\x10\x02\x12\x1b\n\x17\x44\x41TASET_PERMISSION_EDIT\x10\x03*v\n\nVisibility\x12\x1a\n\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x16\n\x12VISIBILITY_PRIVATE\x10\x01\x12\x1d\n\x19VISIBILITY_SHARED_WITH_ME\x10\x02\x12\x15\n\x11VISIBILITY_PUBLIC\x10\x03\x42n\n\x0f\x63om.datasets.v1B\tCoreProtoP\x01\xa2\x02\x03\x44XX\xaa\x02\x0b\x44\x61tasets.V1\xca\x02\x0b\x44\x61tasets\\V1\xe2\x02\x17\x44\x61tasets\\V1\\GPBMetadata\xea\x02\x0c\x44\x61tasets::V1\x92\x03\x02\x08\x02\x62\x08\x65\x64itionsp\xe8\x07')
32
32
 
33
33
  _globals = globals()
34
34
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -48,10 +48,10 @@ if not _descriptor._USE_C_DESCRIPTORS:
48
48
  _globals['_COLLECTIONINFO'].fields_by_name['availability']._serialized_options = b'\252\001\002\010\001'
49
49
  _globals['_COLLECTIONINFO'].fields_by_name['count']._loaded_options = None
50
50
  _globals['_COLLECTIONINFO'].fields_by_name['count']._serialized_options = b'\252\001\002\010\001'
51
- _globals['_DATASETPERMISSION']._serialized_start=1766
52
- _globals['_DATASETPERMISSION']._serialized_end=1921
53
- _globals['_VISIBILITY']._serialized_start=1923
54
- _globals['_VISIBILITY']._serialized_end=2041
51
+ _globals['_DATASETPERMISSION']._serialized_start=1829
52
+ _globals['_DATASETPERMISSION']._serialized_end=1984
53
+ _globals['_VISIBILITY']._serialized_start=1986
54
+ _globals['_VISIBILITY']._serialized_end=2104
55
55
  _globals['_LEGACYPAGINATION']._serialized_start=149
56
56
  _globals['_LEGACYPAGINATION']._serialized_end=242
57
57
  _globals['_ANY']._serialized_start=244
@@ -71,7 +71,7 @@ if not _descriptor._USE_C_DESCRIPTORS:
71
71
  _globals['_COLLECTIONINFOS']._serialized_start=1090
72
72
  _globals['_COLLECTIONINFOS']._serialized_end=1156
73
73
  _globals['_DATASET']._serialized_start=1159
74
- _globals['_DATASET']._serialized_end=1600
75
- _globals['_DATASETGROUP']._serialized_start=1603
76
- _globals['_DATASETGROUP']._serialized_end=1763
74
+ _globals['_DATASET']._serialized_end=1663
75
+ _globals['_DATASETGROUP']._serialized_start=1666
76
+ _globals['_DATASETGROUP']._serialized_end=1826
77
77
  # @@protoc_insertion_point(module_scope)
@@ -112,7 +112,7 @@ class CollectionInfos(_message.Message):
112
112
  def __init__(self, data: _Optional[_Iterable[_Union[CollectionInfo, _Mapping]]] = ...) -> None: ...
113
113
 
114
114
  class Dataset(_message.Message):
115
- __slots__ = ("id", "group_id", "type", "code_name", "name", "summary", "icon", "description", "permissions", "visibility", "slug", "type_editable")
115
+ __slots__ = ("id", "group_id", "type", "code_name", "name", "summary", "icon", "description", "permissions", "visibility", "slug", "type_editable", "collections")
116
116
  ID_FIELD_NUMBER: _ClassVar[int]
117
117
  GROUP_ID_FIELD_NUMBER: _ClassVar[int]
118
118
  TYPE_FIELD_NUMBER: _ClassVar[int]
@@ -125,6 +125,7 @@ class Dataset(_message.Message):
125
125
  VISIBILITY_FIELD_NUMBER: _ClassVar[int]
126
126
  SLUG_FIELD_NUMBER: _ClassVar[int]
127
127
  TYPE_EDITABLE_FIELD_NUMBER: _ClassVar[int]
128
+ COLLECTIONS_FIELD_NUMBER: _ClassVar[int]
128
129
  id: _id_pb2.ID
129
130
  group_id: _id_pb2.ID
130
131
  type: _dataset_type_pb2.AnnotatedType
@@ -137,7 +138,8 @@ class Dataset(_message.Message):
137
138
  visibility: Visibility
138
139
  slug: str
139
140
  type_editable: bool
140
- def __init__(self, id: _Optional[_Union[_id_pb2.ID, _Mapping]] = ..., group_id: _Optional[_Union[_id_pb2.ID, _Mapping]] = ..., type: _Optional[_Union[_dataset_type_pb2.AnnotatedType, _Mapping]] = ..., code_name: _Optional[str] = ..., name: _Optional[str] = ..., summary: _Optional[str] = ..., icon: _Optional[str] = ..., description: _Optional[str] = ..., permissions: _Optional[_Iterable[_Union[DatasetPermission, str]]] = ..., visibility: _Optional[_Union[Visibility, str]] = ..., slug: _Optional[str] = ..., type_editable: bool = ...) -> None: ...
141
+ collections: _containers.RepeatedCompositeFieldContainer[CollectionInfo]
142
+ def __init__(self, id: _Optional[_Union[_id_pb2.ID, _Mapping]] = ..., group_id: _Optional[_Union[_id_pb2.ID, _Mapping]] = ..., type: _Optional[_Union[_dataset_type_pb2.AnnotatedType, _Mapping]] = ..., code_name: _Optional[str] = ..., name: _Optional[str] = ..., summary: _Optional[str] = ..., icon: _Optional[str] = ..., description: _Optional[str] = ..., permissions: _Optional[_Iterable[_Union[DatasetPermission, str]]] = ..., visibility: _Optional[_Union[Visibility, str]] = ..., slug: _Optional[str] = ..., type_editable: bool = ..., collections: _Optional[_Iterable[_Union[CollectionInfo, _Mapping]]] = ...) -> None: ...
141
143
 
142
144
  class DatasetGroup(_message.Message):
143
145
  __slots__ = ("id", "parent_id", "code_name", "name", "icon")
@@ -3,12 +3,23 @@ import sys
3
3
  from importlib.metadata import distributions
4
4
  from uuid import UUID
5
5
 
6
+ from google.protobuf import timestamp_pb2
7
+ from google.protobuf.descriptor_pb2 import FieldDescriptorProto
6
8
  from promise import Promise
7
9
 
8
10
  from tilebox.datasets.data.collection import CollectionInfo
9
11
  from tilebox.datasets.data.data_access import QueryFilters
10
12
  from tilebox.datasets.data.datapoint import AnyMessage, IngestResponse, QueryResultPage
11
- from tilebox.datasets.data.datasets import Dataset, ListDatasetsResponse
13
+ from tilebox.datasets.data.datasets import (
14
+ Dataset,
15
+ DatasetKind,
16
+ DatasetType,
17
+ Field,
18
+ FieldAnnotation,
19
+ FieldDict,
20
+ ListDatasetsResponse,
21
+ )
22
+ from tilebox.datasets.datasets.v1 import well_known_types_pb2
12
23
  from tilebox.datasets.datasets.v1.collections_pb2 import (
13
24
  CreateCollectionRequest,
14
25
  DeleteCollectionRequest,
@@ -20,7 +31,13 @@ from tilebox.datasets.datasets.v1.data_access_pb2 import QueryByIDRequest, Query
20
31
  from tilebox.datasets.datasets.v1.data_access_pb2_grpc import DataAccessServiceStub
21
32
  from tilebox.datasets.datasets.v1.data_ingestion_pb2 import DeleteRequest, IngestRequest
22
33
  from tilebox.datasets.datasets.v1.data_ingestion_pb2_grpc import DataIngestionServiceStub
23
- from tilebox.datasets.datasets.v1.datasets_pb2 import ClientInfo, GetDatasetRequest, ListDatasetsRequest, Package
34
+ from tilebox.datasets.datasets.v1.datasets_pb2 import (
35
+ ClientInfo,
36
+ CreateDatasetRequest,
37
+ GetDatasetRequest,
38
+ ListDatasetsRequest,
39
+ Package,
40
+ )
24
41
  from tilebox.datasets.datasets.v1.datasets_pb2_grpc import DatasetServiceStub
25
42
  from tilebox.datasets.query.pagination import Pagination
26
43
  from tilebox.datasets.tilebox.v1 import id_pb2
@@ -46,6 +63,25 @@ class TileboxDatasetService:
46
63
  self._data_access_service = data_access_service_stub
47
64
  self._data_ingestion_service = data_ingestion_service_stub
48
65
 
66
+ def create_dataset(
67
+ self, kind: DatasetKind, code_name: str, fields: list[FieldDict], name: str, summary: str
68
+ ) -> Promise[Dataset]:
69
+ """Create a new dataset.
70
+
71
+ Args:
72
+ kind: The kind of the dataset.
73
+ code_name: The code name of the dataset.
74
+ fields: The fields of the dataset.
75
+ name: The name of the dataset.
76
+ summary: A short summary of the dataset.
77
+
78
+ Returns:
79
+ The created dataset.
80
+ """
81
+ dataset_type = DatasetType(kind, _REQUIRED_FIELDS_PER_DATASET_KIND[kind] + [Field.from_dict(f) for f in fields])
82
+ req = CreateDatasetRequest(name=name, type=dataset_type.to_message(), summary=summary, code_name=code_name)
83
+ return Promise.resolve(self._dataset_service.CreateDataset(req)).then(Dataset.from_message)
84
+
49
85
  def list_datasets(self) -> Promise[ListDatasetsResponse]:
50
86
  """List all datasets and dataset groups."""
51
87
  return Promise.resolve(
@@ -202,3 +238,65 @@ def _environment_info() -> str:
202
238
  return f"Google Colab using python {python_version}"
203
239
 
204
240
  return f"Unknown IPython using python {python_version}"
241
+
242
+
243
+ _time_field = Field(
244
+ descriptor=FieldDescriptorProto(
245
+ name="time",
246
+ label=FieldDescriptorProto.Label.LABEL_OPTIONAL,
247
+ type=FieldDescriptorProto.TYPE_MESSAGE,
248
+ type_name=f".{timestamp_pb2.Timestamp.DESCRIPTOR.full_name}",
249
+ ),
250
+ annotation=FieldAnnotation(
251
+ description="The timestamp associated with each data point.",
252
+ example_value="2022-10-17T14:35:28Z",
253
+ ),
254
+ queryable=False,
255
+ )
256
+
257
+ _id_field = Field(
258
+ descriptor=FieldDescriptorProto(
259
+ name="id",
260
+ label=FieldDescriptorProto.Label.LABEL_OPTIONAL,
261
+ type=FieldDescriptorProto.TYPE_MESSAGE,
262
+ type_name=f".{well_known_types_pb2.UUID.DESCRIPTOR.full_name}",
263
+ ),
264
+ annotation=FieldAnnotation(
265
+ description="A universally unique identifier (UUID) that uniquely identifies each data point, automatically generated by Tilebox.",
266
+ example_value="4e8a2836-72f8-4ac2-a9e9-cbe3492ef60c",
267
+ ),
268
+ queryable=False,
269
+ )
270
+
271
+ _ingestion_time_field = Field(
272
+ descriptor=FieldDescriptorProto(
273
+ name="ingestion_time",
274
+ label=FieldDescriptorProto.Label.LABEL_OPTIONAL,
275
+ type=FieldDescriptorProto.TYPE_MESSAGE,
276
+ type_name=f".{timestamp_pb2.Timestamp.DESCRIPTOR.full_name}",
277
+ ),
278
+ annotation=FieldAnnotation(
279
+ description="The time the data point was ingested into the Tilebox API, automatically generated by Tilebox.",
280
+ example_value="2022-10-17T14:35:28Z",
281
+ ),
282
+ queryable=False,
283
+ )
284
+
285
+ _geometry_field = Field(
286
+ descriptor=FieldDescriptorProto(
287
+ name="geometry",
288
+ label=FieldDescriptorProto.Label.LABEL_OPTIONAL,
289
+ type=FieldDescriptorProto.TYPE_MESSAGE,
290
+ type_name=f".{well_known_types_pb2.Geometry.DESCRIPTOR.full_name}",
291
+ ),
292
+ annotation=FieldAnnotation(
293
+ description="The geometry associated with each data point.",
294
+ example_value="POLYGON ((112.345 -36.789, ...))",
295
+ ),
296
+ queryable=False,
297
+ )
298
+
299
+ _REQUIRED_FIELDS_PER_DATASET_KIND: dict[DatasetKind, list[Field]] = {
300
+ DatasetKind.TEMPORAL: [_time_field, _id_field, _ingestion_time_field],
301
+ DatasetKind.SPATIOTEMPORAL: [_time_field, _id_field, _ingestion_time_field, _geometry_field],
302
+ }
@@ -4,6 +4,7 @@ from _tilebox.grpc.channel import open_channel
4
4
  from _tilebox.grpc.error import with_pythonic_errors
5
5
  from tilebox.datasets.client import Client as BaseClient
6
6
  from tilebox.datasets.client import token_from_env
7
+ from tilebox.datasets.data.datasets import DatasetKind, FieldDict
7
8
  from tilebox.datasets.datasets.v1.collections_pb2_grpc import CollectionServiceStub
8
9
  from tilebox.datasets.datasets.v1.data_access_pb2_grpc import DataAccessServiceStub
9
10
  from tilebox.datasets.datasets.v1.data_ingestion_pb2_grpc import DataIngestionServiceStub
@@ -32,10 +33,47 @@ class Client:
32
33
  )
33
34
  self._client = BaseClient(service)
34
35
 
36
+ def create_dataset(
37
+ self,
38
+ kind: DatasetKind,
39
+ code_name: str,
40
+ fields: list[FieldDict],
41
+ *,
42
+ name: str | None = None,
43
+ description: str | None = None,
44
+ ) -> DatasetClient:
45
+ """Create a new dataset.
46
+
47
+ Args:
48
+ kind: The kind of the dataset.
49
+ code_name: The code name of the dataset.
50
+ fields: The fields of the dataset.
51
+ name: The name of the dataset. Defaults to the code name.
52
+ description: A short description of the dataset. Optional.
53
+
54
+ Returns:
55
+ The created dataset.
56
+ """
57
+ if name is None:
58
+ name = code_name
59
+ if description is None:
60
+ description = ""
61
+
62
+ return self._client.create_dataset(kind, code_name, fields, name, description, DatasetClient).get()
63
+
35
64
  def datasets(self) -> Group:
65
+ """Fetch all available datasets."""
36
66
  return self._client.datasets(DatasetClient).get()
37
67
 
38
68
  def dataset(self, slug: str) -> DatasetClient:
69
+ """Get a dataset by its slug, e.g. `open_data.copernicus.sentinel1_sar`.
70
+
71
+ Args:
72
+ slug: The slug of the dataset.
73
+
74
+ Returns:
75
+ The dataset if it exists.
76
+ """
39
77
  return self._client.dataset(slug, DatasetClient).get()
40
78
 
41
79
  def _dataset_by_id(self, dataset_id: str | UUID) -> DatasetClient:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilebox-datasets
3
- Version: 0.44.0
3
+ Version: 0.46.0
4
4
  Summary: Access Tilebox datasets from Python
5
5
  Project-URL: Homepage, https://tilebox.com
6
6
  Project-URL: Documentation, https://docs.tilebox.com/datasets/introduction
@@ -1,12 +1,12 @@
1
1
  tilebox/datasets/__init__.py,sha256=4fI6ErCVb15KD_iPWIM6TUXTPgU8qrJVx3Cx7m7jeC8,824
2
- tilebox/datasets/client.py,sha256=RieyTkoUED5cDFsLcakeO4LZ3Yrq7WtRqG5ZSH7mJPM,4076
2
+ tilebox/datasets/client.py,sha256=0RuBAytG9dI-BKpLmb0DbBxhWY9KCOFRpC-3lUsiy1c,4242
3
3
  tilebox/datasets/group.py,sha256=DoGl4w7Viy-l4kE9580dJOymP_B2pj3LRqvMNxvrYmU,1884
4
4
  tilebox/datasets/message_pool.py,sha256=5Hkd2xqgcpQsYY4IySNRGnvAyl2tsyhH21fTAu3EVXg,1272
5
5
  tilebox/datasets/progress.py,sha256=5w_kmrg_aKcW4qluOJu0bPnMP2tV_JA3EZMgk1GDYJM,3531
6
- tilebox/datasets/service.py,sha256=w5Vo5EXN6f_moifa9xLz4dS5en6wB5kNRHZSA-CoXcE,8865
6
+ tilebox/datasets/service.py,sha256=Oo_yJNomUqS_CCbBW132uNyWT_sjjG7_W_bqLJIE4FQ,12195
7
7
  tilebox/datasets/uuid.py,sha256=pqtp5GMHM41KEKZHPdwrHVVThY9VDa7BPbCogrM01ZU,1107
8
8
  tilebox/datasets/aio/__init__.py,sha256=0x_gddLgDsUCdl8MMZj4MPH1lp4HuOrExMHTjIFmM6s,405
9
- tilebox/datasets/aio/client.py,sha256=fr7rtBwVmKihfKQ_pmwfbMascGLbop0liYr4hb3o1eg,2130
9
+ tilebox/datasets/aio/client.py,sha256=Lit7sD1vy7jedhv16oltUf7r9y7mSwoZAAKrG_I60CM,3303
10
10
  tilebox/datasets/aio/dataset.py,sha256=CBiEhT7Pex5JcYulCkKl4DX80oGj6jQB1uUfVV_F-zo,22511
11
11
  tilebox/datasets/aio/pagination.py,sha256=dqxnG1UIvXQwhS5XZDlnmtiy_zat5j5E7xeucqI1dZU,6111
12
12
  tilebox/datasets/aio/timeseries.py,sha256=iQqIyh9TPL_gJz18GCxmtFJEwObR9S2rPsUohFYM8wQ,301
@@ -17,14 +17,14 @@ tilebox/datasets/data/__init__.py,sha256=0PFUikRaYq-jwJSx0rZOUrGj5LF2NMgQv5BQkqY
17
17
  tilebox/datasets/data/collection.py,sha256=OfMsaq02FPk4btVJ3fYF6a8WEVUrjWZAHZffqSwuFbA,2667
18
18
  tilebox/datasets/data/data_access.py,sha256=rKxnSjzLvzco-GHJ4sRC4AJWkx5vSK13Y2o90_0sV-Q,5599
19
19
  tilebox/datasets/data/datapoint.py,sha256=5FOTzPtxWHt0DDivCGv8ZoWbbETkNMEbET6IFHwD8X0,3712
20
- tilebox/datasets/data/datasets.py,sha256=sJ1IAysUOizniJCORICq8wUacVMx0YsN9XLW85n3suI,4234
20
+ tilebox/datasets/data/datasets.py,sha256=K6jmP68gpxMF_U--5JYL2NgEJn80S-oKasfOQ2RYbfU,8570
21
21
  tilebox/datasets/data/time_interval.py,sha256=Ws0oGOUtG9UlNzRVHWxrJu3aMAfOHvMUbemz4VEpg6E,501
22
22
  tilebox/datasets/data/timeseries.py,sha256=eT-J0ySyQZpuctEkG3Otxhd9i-WF4ppDiRlRHLnB-9A,3611
23
23
  tilebox/datasets/datasets/v1/collections_pb2.py,sha256=FRFOmFrI9Qg9yDOqrTseoRzEW-jzt7ux8W-as93_Hiw,5834
24
24
  tilebox/datasets/datasets/v1/collections_pb2.pyi,sha256=WcF7YorsL8kkd7T8XE525F2Xf6laCB4Dgt5-IEpDKlo,2404
25
25
  tilebox/datasets/datasets/v1/collections_pb2_grpc.py,sha256=9cNfk4p6kAFycpps0hGTBtYUfgGnds4dl5y3T_rHavc,8972
26
- tilebox/datasets/datasets/v1/core_pb2.py,sha256=6RixE8n4Wd1QxUmb_jDOozyhqIv9ydZ9jmQI4mXuWe0,7829
27
- tilebox/datasets/datasets/v1/core_pb2.pyi,sha256=wTo-RhRp8-e3AuhN5zWpiajQN_tEKh-5jlJ85i_v8rg,7381
26
+ tilebox/datasets/datasets/v1/core_pb2.py,sha256=xWlHPx5CF9mxYPKIsOQb0XPGMO90T1PymwIqB5NmvWg,7926
27
+ tilebox/datasets/datasets/v1/core_pb2.pyi,sha256=uWOCsXMVIC3JQyr8tu1ZjN5isDbEM2kBSQpW_Xi_3EQ,7593
28
28
  tilebox/datasets/datasets/v1/core_pb2_grpc.py,sha256=xYOs94SXiNYAlFodACnsXW5QovLsHY5tCk3p76RH5Zc,158
29
29
  tilebox/datasets/datasets/v1/data_access_pb2.py,sha256=JRtVN82R5JE9M5lkRlU-oSdwM9_i-ZuzZwexAYI2Pmk,8514
30
30
  tilebox/datasets/datasets/v1/data_access_pb2.pyi,sha256=2l6K3InougubCNfbk61oRgQQMWVMVBmoECrsZZ_9aao,5938
@@ -53,7 +53,7 @@ tilebox/datasets/query/id_interval.py,sha256=Ha3Rm92hZugQXNzyfdFUROT1pTJ1ZBIISqT
53
53
  tilebox/datasets/query/pagination.py,sha256=0kaQI6v9sJnDJblP3VJn6erPbkP_LSwegFRSCzINGY0,774
54
54
  tilebox/datasets/query/time_interval.py,sha256=1Y_ewpGxIUJ1KSkAhoceH0madfsxS-F-NSHY2yM5GZ8,10018
55
55
  tilebox/datasets/sync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
- tilebox/datasets/sync/client.py,sha256=Z_ZbsWxnL5qIvIFvmCT8yP7VEIVIp0MAJK94OtsFP8g,2105
56
+ tilebox/datasets/sync/client.py,sha256=hr5ZAcvCFGqJu3AREIg0RDFBnc8dOmTIEgBWkGvgKUA,3272
57
57
  tilebox/datasets/sync/dataset.py,sha256=wh8grBQZJAPZ7_X_8Ui67hK6v4uGAPC2gx02PSwbUgE,22174
58
58
  tilebox/datasets/sync/pagination.py,sha256=IOSbpNTlv3Fx9QLdBMZHJxZSWeKJNLOVWkmSoKJHIcw,6025
59
59
  tilebox/datasets/sync/timeseries.py,sha256=4nTP8_tmv6V7PXTUNzzlbzlxv0OXo_IqVLtSdJpUOW0,303
@@ -63,6 +63,6 @@ tilebox/datasets/tilebox/v1/id_pb2_grpc.py,sha256=xYOs94SXiNYAlFodACnsXW5QovLsHY
63
63
  tilebox/datasets/tilebox/v1/query_pb2.py,sha256=l60DA1setyQhdBbZ_jgG8Pw3ourUSxXWU5P8AACYlpk,3444
64
64
  tilebox/datasets/tilebox/v1/query_pb2.pyi,sha256=f-u60POkJqzssOmCEbOrD5fam9_86c6MdY_CzpnZZk0,2061
65
65
  tilebox/datasets/tilebox/v1/query_pb2_grpc.py,sha256=xYOs94SXiNYAlFodACnsXW5QovLsHY5tCk3p76RH5Zc,158
66
- tilebox_datasets-0.44.0.dist-info/METADATA,sha256=9UMi4jo2a7r262tMTuQFYq8Asmt2iXxNrRGGEKLdn4o,4234
67
- tilebox_datasets-0.44.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
68
- tilebox_datasets-0.44.0.dist-info/RECORD,,
66
+ tilebox_datasets-0.46.0.dist-info/METADATA,sha256=az6VLDm7YiPyq8izUHYLQ3TUaa3HWnH1NvyFxvvZ3-o,4234
67
+ tilebox_datasets-0.46.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
68
+ tilebox_datasets-0.46.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any