geobox 2.3.0__py3-none-any.whl → 2.4.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.
Files changed (65) hide show
  1. geobox/aio/api.py +153 -7
  2. geobox/aio/apikey.py +0 -26
  3. geobox/aio/attachment.py +5 -31
  4. geobox/aio/basemap.py +2 -30
  5. geobox/aio/dashboard.py +0 -26
  6. geobox/aio/feature.py +172 -17
  7. geobox/aio/field.py +0 -27
  8. geobox/aio/file.py +0 -26
  9. geobox/aio/layout.py +0 -26
  10. geobox/aio/log.py +0 -27
  11. geobox/aio/map.py +0 -26
  12. geobox/aio/model3d.py +0 -26
  13. geobox/aio/mosaic.py +0 -26
  14. geobox/aio/plan.py +0 -26
  15. geobox/aio/query.py +0 -26
  16. geobox/aio/raster.py +0 -26
  17. geobox/aio/scene.py +1 -26
  18. geobox/aio/settings.py +0 -28
  19. geobox/aio/table.py +644 -55
  20. geobox/aio/task.py +0 -27
  21. geobox/aio/tile3d.py +0 -26
  22. geobox/aio/tileset.py +1 -26
  23. geobox/aio/usage.py +0 -32
  24. geobox/aio/user.py +0 -59
  25. geobox/aio/vector_tool.py +49 -0
  26. geobox/aio/vectorlayer.py +8 -34
  27. geobox/aio/version.py +1 -25
  28. geobox/aio/view.py +5 -35
  29. geobox/aio/workflow.py +0 -26
  30. geobox/api.py +152 -7
  31. geobox/apikey.py +0 -26
  32. geobox/attachment.py +0 -26
  33. geobox/basemap.py +0 -28
  34. geobox/dashboard.py +0 -26
  35. geobox/enums.py +11 -1
  36. geobox/feature.py +170 -15
  37. geobox/field.py +0 -28
  38. geobox/file.py +0 -26
  39. geobox/layout.py +0 -26
  40. geobox/log.py +0 -26
  41. geobox/map.py +0 -26
  42. geobox/model3d.py +0 -26
  43. geobox/mosaic.py +0 -26
  44. geobox/plan.py +1 -26
  45. geobox/query.py +1 -26
  46. geobox/raster.py +1 -31
  47. geobox/scene.py +1 -27
  48. geobox/settings.py +1 -29
  49. geobox/table.py +640 -55
  50. geobox/task.py +2 -29
  51. geobox/tile3d.py +0 -26
  52. geobox/tileset.py +1 -26
  53. geobox/usage.py +2 -33
  54. geobox/user.py +1 -59
  55. geobox/vector_tool.py +49 -0
  56. geobox/vectorlayer.py +9 -36
  57. geobox/version.py +1 -26
  58. geobox/view.py +4 -34
  59. geobox/workflow.py +1 -26
  60. {geobox-2.3.0.dist-info → geobox-2.4.0.dist-info}/METADATA +1 -1
  61. geobox-2.4.0.dist-info/RECORD +74 -0
  62. {geobox-2.3.0.dist-info → geobox-2.4.0.dist-info}/WHEEL +1 -1
  63. geobox-2.3.0.dist-info/RECORD +0 -74
  64. {geobox-2.3.0.dist-info → geobox-2.4.0.dist-info}/licenses/LICENSE +0 -0
  65. {geobox-2.3.0.dist-info → geobox-2.4.0.dist-info}/top_level.txt +0 -0
geobox/aio/api.py CHANGED
@@ -32,7 +32,7 @@ from .attachment import AsyncAttachment
32
32
  from .apikey import AsyncApiKey
33
33
  from .log import AsyncLog
34
34
  from .usage import AsyncUsage, UsageScale, UsageParam
35
- from .table import AsyncTable
35
+ from .table import AsyncTable, AsyncRelationship, RelationshipCardinality, RelationshipEndpoint
36
36
  from ..exception import AuthenticationError, ApiRequestError, NotFoundError, ValidationError, ServerError, AuthorizationError
37
37
  from ..utils import join_url_params
38
38
 
@@ -130,11 +130,10 @@ class AsyncGeoboxClient:
130
130
  """
131
131
  Constructs all the necessary attributes for the Api object.
132
132
  """
133
- self.username = os.getenv('GEOBOX_USERNAME') if os.getenv('GEOBOX_USERNAME') else username
134
- self.password = os.getenv('GEOBOX_PASSWORD') if os.getenv('GEOBOX_PASSWORD') else password
135
- self.access_token = os.getenv('GEOBOX_ACCESS_TOKEN') if os.getenv('GEOBOX_ACCESS_TOKEN') else access_token
136
- self.apikey = os.getenv('GEOBOX_APIKEY') if os.getenv('GEOBOX_APIKEY') else apikey
137
-
133
+ self.username = username if username else os.getenv('GEOBOX_USERNAME')
134
+ self.password = password if password else os.getenv('GEOBOX_PASSWORD')
135
+ self.access_token = access_token if access_token else os.getenv('GEOBOX_ACCESS_TOKEN')
136
+ self.apikey = apikey if apikey else os.getenv('GEOBOX_APIKEY')
138
137
  self.session = AsyncRequestSession(access_token=self.access_token)
139
138
 
140
139
  host = host.lower()
@@ -2766,4 +2765,151 @@ class AsyncGeoboxClient:
2766
2765
  >>> async with AsyncGeoboxClient() as client:
2767
2766
  >>> table = await client.get_table_by_name(name='test')
2768
2767
  """
2769
- return await AsyncTable.get_table_by_name(self, name, user_id)
2768
+ return await AsyncTable.get_table_by_name(self, name, user_id)
2769
+
2770
+
2771
+ async def get_relationships(
2772
+ self,
2773
+ **kwargs,
2774
+ ) -> Union[List['AsyncRelationship'], int]:
2775
+ """
2776
+ [async] Get a list of relationships with optional filtering and pagination.
2777
+
2778
+ Keyword Args:
2779
+ q (str): query filter based on OGC CQL standard. e.g. "field1 LIKE '%GIS%' AND created_at > '2021-01-01'"
2780
+ search (str): search term for keyword-based searching among search_fields or all textual fields if search_fields does not have value. NOTE: if q param is defined this param will be ignored
2781
+ search_fields (str): comma separated list of fields for searching
2782
+ order_by (str): comma separated list of fields for sorting results [field1 A|D, field2 A|D, …]. e.g. name A, type D. NOTE: "A" denotes ascending order and "D" denotes descending order.
2783
+ return_count (bool): Whether to return total count. default: False.
2784
+ skip (int): Number of items to skip. default: 0
2785
+ limit (int): Number of items to return. default: 10
2786
+ user_id (int): Specific user. privileges required
2787
+ shared (bool): Whether to return shared tables. default: False
2788
+
2789
+ Returns:
2790
+ List[AsyncRelationship] | int: A list of relationship instances or the total number of relationships.
2791
+
2792
+ Example:
2793
+ >>> from geobox.aio import AsyncGeoboxClient
2794
+ >>> async with AsyncGeoboxClient() as client:
2795
+ >>> relationships = await client.get_relationships(q="name LIKE '%My relationship%'")
2796
+ """
2797
+ return await AsyncRelationship.get_relationships(self, **kwargs)
2798
+
2799
+
2800
+ async def create_relationship(
2801
+ self,
2802
+ name: str,
2803
+ cardinality: 'RelationshipCardinality',
2804
+ *,
2805
+ source: 'RelationshipEndpoint',
2806
+ target: 'RelationshipEndpoint',
2807
+ relation_table: Optional['AsyncTable'] = None,
2808
+ display_name: Optional[str] = None,
2809
+ description: Optional[str] = None,
2810
+ user_id: Optional[int] = None,
2811
+ ) -> 'AsyncRelationship':
2812
+ """
2813
+ [async] Create a new AsyncRelationship
2814
+
2815
+ Args:
2816
+ name (str): name of the relationship
2817
+ cardinality (RelationshipCardinality): One to One, One to Many, or Many to Many
2818
+
2819
+ Keyword Args:
2820
+ source (RelationshipEndpoint): Definition of the source side of the relationship, including the table (or layer), field, and foreign-key field
2821
+ target (RelationshipEndpoint): Definition of the target side of the relationship, including the table (or layer), field, and foreign-key field
2822
+ relation_table (AsyncTable, optional): The table that stores the relationship metadata or join records. (Required for Many-to-Many relationships)
2823
+ display_name (str, optional): Human-readable name for the relationship
2824
+ description (str, optional): the description of the relationship
2825
+ user_id (int, optional): Specific user. privileges required.
2826
+
2827
+ Returns:
2828
+ AsyncRelationship: a relationship instance
2829
+
2830
+ Example:
2831
+ >>> from geobox.aio import AsyncGeoboxClient
2832
+ >>> from geobox.aio.table import RelationshipEndpoint, RelationshipCardinality
2833
+ >>> async with AsyncGeoboxClient() as client:
2834
+ >>> source = RelationshipEndpoint(
2835
+ ... table=client.get_table_by_name('owner'),
2836
+ ... field="name", # on source table
2837
+ ... fk_field="book_name", # on relation table
2838
+ ... )
2839
+ >>> target = RelationshipEndpoint(
2840
+ ... table=client.get_table_by_name('parcel'),
2841
+ ... field="name", # on target table
2842
+ ... fk_field="author_name", # on relation table
2843
+ ... )
2844
+ >>> relationship = await client.create_relationship(
2845
+ ... name="owner_parcel",
2846
+ ... cardinality=RelationshipCardinality.ManytoMany,
2847
+ ... source=source,
2848
+ ... target=target,
2849
+ ... relation_table=client.get_table_by_name('owner_parcel'),
2850
+ ... )
2851
+ """
2852
+ return await AsyncRelationship.create_relationship(
2853
+ self,
2854
+ name=name,
2855
+ cardinality=cardinality,
2856
+ source=source,
2857
+ target=target,
2858
+ relation_table=relation_table,
2859
+ display_name=display_name,
2860
+ description=description,
2861
+ user_id=user_id,
2862
+ )
2863
+
2864
+
2865
+ async def get_relationship(
2866
+ self,
2867
+ uuid: str,
2868
+ user_id: Optional[int] = None,
2869
+ ) -> 'AsyncRelationship':
2870
+ """
2871
+ [async] Get a relationship by UUID.
2872
+
2873
+ Args:
2874
+ uuid (str): The UUID of the relationship to get.
2875
+ user_id (int, optional): Specific user. privileges required.
2876
+
2877
+ Returns:
2878
+ AsyncRelationship: The AsyncRelationship object.
2879
+
2880
+ Raises:
2881
+ NotFoundError: If the AsyncRelationship with the specified UUID is not found.
2882
+
2883
+ Example:
2884
+ >>> from geobox.aio import AsyncGeoboxClient
2885
+ >>> async with AsyncGeoboxClient() as client:
2886
+ >>> relationship = await client.get_relationship(uuid="12345678-1234-5678-1234-567812345678")
2887
+ """
2888
+ return await AsyncRelationship.get_relationship(
2889
+ self,
2890
+ uuid=uuid,
2891
+ user_id=user_id,
2892
+ )
2893
+
2894
+
2895
+ async def get_relationship_by_name(
2896
+ self,
2897
+ name: str,
2898
+ user_id: Optional[int] = None,
2899
+ ) -> Union['AsyncRelationship', None]:
2900
+ """
2901
+ [async] Get a relationship by name
2902
+
2903
+ Args:
2904
+ name (str): the name of the relationship to get
2905
+ user_id (int, optional): specific user. privileges required.
2906
+
2907
+ Returns:
2908
+ AsyncRelationship | None: returns the relationship if a relationship matches the given name, else None
2909
+
2910
+ Example:
2911
+ >>> from geobox.aio import AsyncGeoboxClient
2912
+ >>> async with AsyncGeoboxClient() as client:
2913
+ >>> relationship = await client.get_relationship_by_name(name='test')
2914
+ """
2915
+ return await AsyncRelationship.get_relationship_by_name(self, name=name, user_id=user_id)
geobox/aio/apikey.py CHANGED
@@ -6,8 +6,6 @@ from ..utils import clean_data
6
6
 
7
7
  if TYPE_CHECKING:
8
8
  from . import AsyncGeoboxClient
9
- from ..api import GeoboxClient
10
- from ..apikey import ApiKey
11
9
 
12
10
 
13
11
  class AsyncApiKey(AsyncBase):
@@ -237,27 +235,3 @@ class AsyncApiKey(AsyncBase):
237
235
  endpoint = f"{self.endpoint}/grant"
238
236
  await self.api.post(endpoint)
239
237
  self.data['revoked'] = False
240
-
241
-
242
- def to_sync(self, sync_client: 'GeoboxClient') -> 'ApiKey':
243
- """
244
- Switch to sync version of the apikey instance to have access to the sync methods
245
-
246
- Args:
247
- sync_client (GeoboxClient): The sync version of the GeoboxClient instance for making requests.
248
-
249
- Returns:
250
- ApiKey: the sync instance of the apikey.
251
-
252
- Example:
253
- >>> from geobox import Geoboxclient
254
- >>> from geobox.aio import AsyncGeoboxClient
255
- >>> from geobox.aio.apikey import AsyncApiKey
256
- >>> client = GeoboxClient()
257
- >>> async with AsyncGeoboxClient() as async_client:
258
- >>> apikey = await AsyncApiKey.get_apikey(async_client, key_id=1)
259
- >>> sync_apikey = apikey.to_sync(client)
260
- """
261
- from ..apikey import ApiKey
262
-
263
- return ApiKey(api=sync_client, key_id=self.key_id, data=self.data)
geobox/aio/attachment.py CHANGED
@@ -10,9 +10,7 @@ from ..utils import clean_data
10
10
 
11
11
  if TYPE_CHECKING:
12
12
  from . import AsyncGeoboxClient
13
- from .feature import Feature
14
- from ..api import GeoboxClient
15
- from ..attachment import Attachment
13
+ from .feature import AsyncFeature
16
14
 
17
15
 
18
16
  class AsyncAttachment(AsyncBase):
@@ -126,8 +124,8 @@ class AsyncAttachment(AsyncBase):
126
124
  loc_x: int,
127
125
  loc_y: int,
128
126
  resource: Union['AsyncMap', 'AsyncVectorLayer', 'AsyncVectorLayerView'],
129
- file: 'File',
130
- feature: 'Feature' = None,
127
+ file: 'AsyncFile',
128
+ feature: 'AsyncFeature' = None,
131
129
  display_name: str = None,
132
130
  description: str = None, ) -> 'AsyncAttachment':
133
131
  """
@@ -139,8 +137,8 @@ class AsyncAttachment(AsyncBase):
139
137
  loc_x (int): x parameter of the attachment location.
140
138
  loc_y (int): y parameter of the attachment location.
141
139
  resource (AsyncMap | AsyncVectorLayer | AsyncVectorLayerView): the resource object.
142
- file (File): the file object.
143
- feature (Feature, optional): the feature object.
140
+ file (AsyncFile): the file object.
141
+ feature (AsyncFeature, optional): the feature object.
144
142
  display_name (str, optional): The display name of the scene.
145
143
  description (str, optional): The description of the scene.
146
144
 
@@ -315,27 +313,3 @@ class AsyncAttachment(AsyncBase):
315
313
  >>> attachment.thumbnail
316
314
  """
317
315
  return super()._thumbnail(format='')
318
-
319
-
320
- def to_sync(self, sync_client: 'GeoboxClient') -> 'Attachment':
321
- """
322
- Switch to sync version of the attachment instance to have access to the sync methods
323
-
324
- Args:
325
- sync_client (GeoboxClient): The sync version of the GeoboxClient instance for making requests.
326
-
327
- Returns:
328
- Attachment: the sync instance of the attachment.
329
-
330
- Example:
331
- >>> from geobox import Geoboxclient
332
- >>> from geobox.aio import AsyncGeoboxClient
333
- >>> from geobox.aio.attachment import AsyncAttachment
334
- >>> client = GeoboxClient()
335
- >>> async with AsyncGeoboxClient() as async_client:
336
- >>> attachment = await AsyncAttachment.get_attachment(async_client, uuid="12345678-1234-5678-1234-567812345678")
337
- >>> sync_attachment = attachment.to_sync(client)
338
- """
339
- from ..attachment import Attachment
340
-
341
- return Attachment(api=sync_client, attachment_id=self.attachment_id, data=self.data)
geobox/aio/basemap.py CHANGED
@@ -7,8 +7,6 @@ from ..utils import clean_data
7
7
 
8
8
  if TYPE_CHECKING:
9
9
  from . import AsyncGeoboxClient
10
- from ..api import GeoboxClient
11
- from ..basemap import Basemap
12
10
 
13
11
 
14
12
  class AsyncBasemap(AsyncBase):
@@ -143,12 +141,12 @@ class AsyncBasemap(AsyncBase):
143
141
 
144
142
 
145
143
  @classmethod
146
- async def proxy_basemap(cls, api: 'response', url: str) -> None:
144
+ async def proxy_basemap(cls, api: 'AsyncGeoboxClient', url: str) -> None:
147
145
  """
148
146
  [async] Proxy the basemap
149
147
 
150
148
  Args:
151
- api (GeoboxClient): The GeoboxClient instance for making requests.
149
+ api (AsyncGeoboxClient): The GeoboxClient instance for making requests.
152
150
  url (str): the proxy server url.
153
151
 
154
152
  Returns:
@@ -168,29 +166,3 @@ class AsyncBasemap(AsyncBase):
168
166
  query_string = urlencode(param)
169
167
  endpoint = urljoin(cls.BASE_ENDPOINT, f"?{query_string}")
170
168
  await api.get(endpoint)
171
-
172
-
173
- def to_sync(self, sync_client: 'GeoboxClient') -> 'Basemap':
174
- """
175
- Switch to sync version of the basemap instance to have access to the sync methods
176
-
177
- Args:
178
- sync_client (GeoboxClient): The sync version of the GeoboxClient instance for making requests.
179
-
180
- Returns:
181
- Basemap: the sync instance of the basemap.
182
-
183
- Example:
184
- >>> from geobox import Geoboxclient
185
- >>> from geobox.aio import AsyncGeoboxClient
186
- >>> from geobox.aio.basemap import AsyncBasemap
187
- >>> client = GeoboxClient()
188
- >>> async with AsyncGeoboxClient() as async_client:
189
- >>> basemap = await AsyncBasemap.get_basemap(async_client, name='test')
190
- or
191
- >>> basemap = await async_client.get_basemap(name='test')
192
- >>> sync_basemap = basemap.to_sync(client)
193
- """
194
- from ..basemap import Basemap
195
-
196
- return Basemap(api=sync_client, data=self.data)
geobox/aio/dashboard.py CHANGED
@@ -5,8 +5,6 @@ from .base import AsyncBase
5
5
  if TYPE_CHECKING:
6
6
  from . import AsyncGeoboxClient
7
7
  from .user import AsyncUser
8
- from ..api import GeoboxClient
9
- from ..dashboard import Dashboard
10
8
 
11
9
 
12
10
  class AsyncDashboard(AsyncBase):
@@ -314,27 +312,3 @@ class AsyncDashboard(AsyncBase):
314
312
  'limit': limit
315
313
  }
316
314
  return await super()._get_shared_users(self.endpoint, params)
317
-
318
-
319
- def to_sync(self, sync_client: 'GeoboxClient') -> 'Dashboard':
320
- """
321
- Switch to sync version of the dashboard instance to have access to the sync methods
322
-
323
- Args:
324
- sync_client (GeoboxClient): The sync version of the GeoboxClient instance for making requests.
325
-
326
- Returns:
327
- Dashboard: the sync instance of the dashboard.
328
-
329
- Example:
330
- >>> from geobox import Geoboxclient
331
- >>> from geobox.aio import AsyncGeoboxClient
332
- >>> from geobox.aio.dashboard import AsyncDashboard
333
- >>> client = GeoboxClient()
334
- >>> async with AsyncGeoboxClient() as async_client:
335
- >>> dashboard = await AsyncDashboard.get_dashboard(async_client, uuid="12345678-1234-5678-1234-567812345678")
336
- >>> sync_dashboard = dashboard.to_sync(client)
337
- """
338
- from ..dashboard import Dashboard
339
-
340
- return Dashboard(api=sync_client, uuid=self.uuid, data=self.data)
geobox/aio/feature.py CHANGED
@@ -1,13 +1,12 @@
1
1
  from urllib.parse import urljoin
2
- from typing import Optional, List, Dict, Any, TYPE_CHECKING
2
+ from typing import Optional, List, Dict, Any, TYPE_CHECKING, Union
3
3
 
4
4
  from .base import AsyncBase
5
5
  from ..enums import FeatureType
6
6
 
7
7
  if TYPE_CHECKING:
8
- from .vectorlayer import VectorLayer
9
- from ..api import GeoboxClient
10
- from ..feature import Feature
8
+ from .vectorlayer import VectorLayer, AsyncVectorLayer
9
+ from .table import AsyncTable, AsyncTableRow, AsyncRelationship
11
10
 
12
11
 
13
12
  class AsyncFeature(AsyncBase):
@@ -522,26 +521,182 @@ class AsyncFeature(AsyncBase):
522
521
  return self
523
522
 
524
523
 
525
- def to_sync(self, sync_client: 'GeoboxClient') -> 'Feature':
524
+ async def _get_other_side_of_relationship(
525
+ self,
526
+ relationship: 'AsyncRelationship',
527
+ ) -> Union['AsyncTable', 'AsyncVectorLayer']:
526
528
  """
527
- [async] Switch to sync version of the feature instance to have access to the sync methods
529
+ [async] Determine which side of a relationship this table is on and return the opposite side.
530
+
531
+ Used internally to navigate bidirectional relationships.
532
+
533
+ Args:
534
+ relationship (AsyncRelationship): The relationship to examine.
535
+
536
+ Returns:
537
+ AsyncTable | AsyncVectorLayer: The endpoint (table or layer) on the opposite side
538
+ of the relationship from this table.
539
+
540
+ Raises:
541
+ ValueError: If this table is not part of the given relationship.
542
+
543
+ Note:
544
+ This method assumes the table is either the source or target,
545
+ not the relation table in Many-to-Many relationships.
546
+ """
547
+ if relationship.source_id == self.layer.id:
548
+ return await relationship.get_target()
549
+
550
+ if relationship.target_id == self.layer.id:
551
+ return await relationship.get_source()
528
552
 
553
+ raise ValueError("Relationship does not involve this table.")
554
+
555
+
556
+ async def _fetch_related_from_target(
557
+ self,
558
+ target: Union['AsyncTable', 'AsyncVectorLayer'],
559
+ relationship_uuid: str,
560
+ ) -> Union[List['AsyncTableRow'], List['AsyncFeature']]:
561
+ """
562
+ [async] Fetch related rows/features from a relationship target.
563
+
564
+ Internal helper that dispatches to the appropriate API method
565
+ based on target type.
566
+
529
567
  Args:
530
- sync_client (GeoboxClient): The sync version of the GeoboxClient instance for making requests.
568
+ target (AsyncTable | AsyncVectorLayer): The target endpoint (Table or VectorLayer) to query.
569
+ relationship_uuid (str): UUID of the relationship to traverse.
570
+
571
+ Raises:
572
+ TypeError: If target is not a Table or VectorLayer.
531
573
 
532
574
  Returns:
533
- Feature: the sync instance of the feature.
575
+ List[AsyncTableRow] | List[AsyncFeature]: Related rows or features.
576
+ """
577
+ from .table import AsyncTable
578
+ from .vectorlayer import AsyncVectorLayer
579
+
580
+ if isinstance(target, AsyncTable):
581
+ return await target.get_rows(
582
+ relationship_uuid=relationship_uuid,
583
+ related_record_id=self.id,
584
+ )
585
+
586
+ if isinstance(target, AsyncVectorLayer):
587
+ return await target.get_features(
588
+ relationship_uuid=relationship_uuid,
589
+ related_record_id=self.id,
590
+ )
591
+
592
+ raise TypeError(f"Unsupported target type: {type(target)}")
593
+
594
+
595
+ async def get_related_records(self,
596
+ relationship_uuid: str,
597
+ ) -> Union[List['AsyncTableRow'], List['AsyncFeature']]:
598
+ """
599
+ [async] Get the related records on the *other side* of the relationship that are linked to this row
600
+
601
+ Args:
602
+ relationship_uuid (str): The uuid of relationship
603
+
604
+ Returns:
605
+ List[AsyncTableRow] | List[AsyncFeature]: a list of the related records
606
+
607
+ Raises:
608
+ ValueError:
609
+ If the given relationship does not involve the current table
610
+ (i.e., this row is neither the source nor the target of the relationship).
611
+
612
+ TypeError:
613
+ If the relationship target type is not supported for fetching
614
+ related records.
534
615
 
535
616
  Example:
536
- >>> from geobox import Geoboxclient
537
617
  >>> from geobox.aio import AsyncGeoboxClient
538
- >>> client = GeoboxClient()
539
- >>> async with AsyncGeoboxClient() as async_client:
540
- >>> layer = await async_client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
541
- >>> feature = await layer.get_feature(id=1, srid=3857)
542
- >>> sync_feature = feature.to_sync(client)
618
+ >>> async with AsyncGeoboxClient() as client:
619
+ >>> layer = await client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
620
+ >>> feature = await layer.get_feature(feature_id=1)
621
+ >>> related_records = await feature.get_related_records(relationship_uuid="12345678-1234-5678-1234-567812345678")
622
+ """
623
+ relationship = await self.api.get_relationship(relationship_uuid)
624
+
625
+ other_side = await self._get_other_side_of_relationship(relationship)
626
+
627
+ return await self._fetch_related_from_target(
628
+ target=other_side,
629
+ relationship_uuid=relationship_uuid,
630
+ )
631
+
632
+
633
+ async def associate_with(
634
+ self,
635
+ relationship_uuid: str,
636
+ *,
637
+ target_ids: Optional[List[int]] = None,
638
+ q: Optional[str] = None,
639
+ ) -> Dict:
543
640
  """
544
- from ..feature import Feature
641
+ [async] Create relationships between the source record and target records
642
+
643
+ Args:
644
+ relationship_uuid (str): the relationship uuid
645
+ target_ids (List[int], optional): a list of target record ids to be associated with the current record
646
+ q (str, optional): query filter on target layer or table to select which target features or rows that are going to be related to the current record
647
+
648
+ Returns:
649
+ Dict: the record association result
545
650
 
546
- sync_layer = self.layer.to_sync(sync_client=sync_client)
547
- return Feature(layer=sync_layer, srid=self.srid, data=self.data)
651
+ Example:
652
+ >>> from geobox.aio import AsyncGeoboxClient
653
+ >>> async with AsyncGeoboxClient() as client:
654
+ >>> layer = await client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
655
+ >>> feature = await layer.get_feature(feature_id=1)
656
+ >>> await feature.associate_with(
657
+ ... relationship_uuid="12345678-1234-5678-1234-567812345678",
658
+ ... target_ids=[1, 2, 3],
659
+ ... )
660
+ """
661
+ relationship = await self.api.get_relationship(uuid=relationship_uuid)
662
+ return await relationship.associate_records(
663
+ source_id=self.id,
664
+ target_ids=target_ids,
665
+ q=q,
666
+ )
667
+
668
+
669
+ async def disassociate_with(
670
+ self,
671
+ relationship_uuid: str,
672
+ *,
673
+ target_ids: Optional[List[int]] = None,
674
+ q: Optional[str] = None,
675
+ ) -> Dict:
676
+ """
677
+ [async] Remove relationships between the source record and target records
678
+
679
+ Args:
680
+ relationship_uuid (str): the relationship uuid
681
+ target_ids (List[int], optional): a list of target record ids to be disassociated with the current record
682
+ q (str, optional): query filter on target layer or table to select which target features or rows that are going to be related to the current record
683
+
684
+ Returns:
685
+ Dict: the record association result
686
+
687
+ Example:
688
+ >>> from geobox.aio import AsyncGeoboxClient
689
+ >>> async with AsyncGeoboxClient() as client:
690
+ >>> layer = await client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
691
+ >>> feature = await layer.get_feature(feature_id=1)
692
+ >>> await feature.disassociate_with(
693
+ ... relationship_uuid="12345678-1234-5678-1234-567812345678",
694
+ ... target_ids=[1, 2, 3],
695
+ ... )
696
+ """
697
+ relationship = await self.api.get_relationship(uuid=relationship_uuid)
698
+ return await relationship.disassociate_records(
699
+ source_id=self.id,
700
+ target_ids=target_ids,
701
+ q=q,
702
+ )
geobox/aio/field.py CHANGED
@@ -8,8 +8,6 @@ from ..enums import FieldType
8
8
  if TYPE_CHECKING:
9
9
  from .api import AsyncGeoboxClient
10
10
  from .vectorlayer import VectorLayer
11
- from ..api import GeoboxClient
12
- from ..field import Field
13
11
 
14
12
 
15
13
  class AsyncField(AsyncBase):
@@ -288,28 +286,3 @@ class AsyncField(AsyncBase):
288
286
 
289
287
  await self.save()
290
288
  return self.domain
291
-
292
-
293
- def to_sync(self, sync_client: 'GeoboxClient') -> 'Field':
294
- """
295
- Switch to sync version of the field instance to have access to the sync methods
296
-
297
- Args:
298
- sync_client (GeoboxClient): The sync version of the GeoboxClient instance for making requests.
299
-
300
- Returns:
301
- Field: the sync instance of the field.
302
-
303
- Example:
304
- >>> from geobox import Geoboxclient
305
- >>> from geobox.aio import AsyncGeoboxClient
306
- >>> client = GeoboxClient()
307
- >>> async with AsyncGeoboxClient() as async_client:
308
- >>> layer = await async_client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
309
- >>> field = await layer.get_field(name='test')
310
- >>> sync_field = field.to_sync(client)
311
- """
312
- from ..field import Field
313
-
314
- sync_layer = self.layer.to_sync(sync_client=sync_client)
315
- return Field(layer=sync_layer, data_type=self.data_type, field_id=self.field_id, data=self.data)
geobox/aio/file.py CHANGED
@@ -16,8 +16,6 @@ from ..exception import ApiRequestError
16
16
  if TYPE_CHECKING:
17
17
  from . import AsyncGeoboxClient
18
18
  from .user import AsyncUser
19
- from ..api import GeoboxClient
20
- from ..file import File
21
19
 
22
20
 
23
21
  class AsyncFile(AsyncBase):
@@ -496,27 +494,3 @@ class AsyncFile(AsyncBase):
496
494
  'limit': limit
497
495
  }
498
496
  return await super()._get_shared_users(self.endpoint, params)
499
-
500
-
501
- def to_sync(self, sync_client: 'GeoboxClient') -> 'File':
502
- """
503
- Switch to sync version of the file instance to have access to the sync methods
504
-
505
- Args:
506
- sync_client (GeoboxClient): The sync version of the GeoboxClient instance for making requests.
507
-
508
- Returns:
509
- File: the async instance of the file.
510
-
511
- Example:
512
- >>> from geobox import Geoboxclient
513
- >>> from geobox.aio import AsyncGeoboxClient
514
- >>> from geobox.aio.file import AsyncFile
515
- >>> client = GeoboxClient()
516
- >>> async with AsyncGeoboxClient() as async_client:
517
- >>> file = await AsyncFile.get_file(async_client, uuid="12345678-1234-5678-1234-567812345678")
518
- >>> sync_file = file.to_sync(client)
519
- """
520
- from ..file import File
521
-
522
- return File(api=sync_client, uuid=self.uuid, data=self.data)