Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions felt_python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
move_map,
create_embed_token,
add_source_layer,
duplicate_map,
# Deprecated
get_map_details,
)
Expand Down Expand Up @@ -47,6 +48,7 @@
from .layer_groups import (
list_layer_groups,
get_layer_group,
update_layer_group,
update_layer_groups,
delete_layer_group,
publish_layer_group,
Expand Down Expand Up @@ -89,6 +91,7 @@
"move_map",
"create_embed_token",
"add_source_layer",
"duplicate_map",
# Layers
"list_layers",
"upload_file",
Expand All @@ -110,6 +113,7 @@
# Layer groups
"list_layer_groups",
"get_layer_group",
"update_layer_group",
"update_layer_groups",
"delete_layer_group",
"publish_layer_group",
Expand Down
44 changes: 44 additions & 0 deletions felt_python/layer_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,50 @@ def delete_layer_group(
)


def update_layer_group(
map_id: str,
layer_group_id: str,
name: str = None,
caption: str = None,
ordering_key: int = None,
visibility_interaction: str = None,
api_token: str | None = None,
):
"""Update a single layer group

Args:
map_id: The ID of the map containing the layer group
layer_group_id: The ID of the layer group to update
name: Optional new name for the layer group
caption: Optional new caption for the layer group
ordering_key: Optional new ordering key for positioning
visibility_interaction: Optional visibility interaction setting
("default", "slider")
api_token: Optional API token

Returns:
The updated layer group
"""
json_payload = {}

if name is not None:
json_payload["name"] = name
if caption is not None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since caption is deprecated (per the doc string), should we raise a warning when used?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh actually it's not deprecated, there's an error in our Open API spec! I'll get that fixed

json_payload["caption"] = caption
if ordering_key is not None:
json_payload["ordering_key"] = ordering_key
if visibility_interaction is not None:
json_payload["visibility_interaction"] = visibility_interaction

response = make_request(
url=GROUP.format(map_id=map_id, layer_group_id=layer_group_id),
method="POST",
json=json_payload,
api_token=api_token,
)
return json.load(response)


def publish_layer_group(
map_id: str,
layer_group_id: str,
Expand Down
43 changes: 43 additions & 0 deletions felt_python/maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
MAP_MOVE = urljoin(BASE_URL, "maps/{map_id}/move")
MAP_EMBED_TOKEN = urljoin(BASE_URL, "maps/{map_id}/embed_token")
MAP_ADD_SOURCE_LAYER = urljoin(BASE_URL, "maps/{map_id}/add_source_layer")
MAP_DUPLICATE = urljoin(BASE_URL, "maps/{map_id}/duplicate")


def create_map(
Expand Down Expand Up @@ -230,3 +231,45 @@ def add_source_layer(
api_token=api_token,
)
return json.load(response)


def duplicate_map(
map_id: str,
title: str = None,
project_id: str = None,
folder_id: str = None,
api_token: str = None,
):
"""Duplicate a map

Args:
map_id: The ID of the map to duplicate
title: Optional title for the duplicated map
project_id: The ID of the project to place the duplicated map in
(mutually exclusive with folder_id)
folder_id: The ID of the folder to place the duplicated map in
(mutually exclusive with project_id)
api_token: Optional API token

Returns:
The duplicated map
"""
if project_id is not None and folder_id is not None:
raise ValueError("Cannot specify both project_id and folder_id")

json_args = {}
if title is not None:
json_args["title"] = title

if project_id is not None:
json_args["destination"] = {"project_id": project_id}
elif folder_id is not None:
json_args["destination"] = {"folder_id": folder_id}

response = make_request(
url=MAP_DUPLICATE.format(map_id=map_id),
method="POST",
json=json_args,
api_token=api_token,
)
return json.load(response)
50 changes: 39 additions & 11 deletions notebooks/layer_groups.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
" delete_map,\n",
" list_layer_groups,\n",
" get_layer_group,\n",
" update_layer_group,\n",
" update_layer_groups,\n",
" delete_layer_group,\n",
" publish_layer_group,\n",
Expand Down Expand Up @@ -204,14 +205,41 @@
"\n",
"result = update_layer_groups(map_id, updated_groups)\n",
"result"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Update layers to assign them to groups\n"
]
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Update a single layer group\n",
"\n",
"You can also update individual layer groups instead of doing bulk updates"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Update just the first group individually\n",
"individual_update_result = update_layer_group(\n",
" map_id=map_id,\n",
" layer_group_id=group1_id,\n",
" name=\"Vector Data (Individual Update)\",\n",
" caption=\"Updated via individual update function\",\n",
" ordering_key=10,\n",
" visibility_interaction=\"slider\"\n",
")\n",
"individual_update_result"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Update layers to assign them to groups\n"
]
},
{
"cell_type": "code",
Expand All @@ -222,11 +250,11 @@
"# Prepare updates for both layers\n",
"layer_updates = [\n",
" {\n",
" \"id\": layer1_id\n",
" \"id\": layer1_id,\n",
" \"layer_group_id\": group1_id,\n",
" },\n",
" {\n",
" \"id\": layer2_id\n",
" \"id\": layer2_id,\n",
" \"layer_group_id\": group2_id,\n",
" }\n",
"]\n",
Expand Down Expand Up @@ -316,4 +344,4 @@
},
"nbformat": 4,
"nbformat_minor": 4
}
}
35 changes: 33 additions & 2 deletions notebooks/maps.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
" resolve_comment,\n",
" delete_comment,\n",
" create_embed_token,\n",
" add_source_layer\n",
" add_source_layer,\n",
" duplicate_map\n",
")\n",
"\n",
"os.environ[\"FELT_API_TOKEN\"] = \"<YOUR_API_TOKEN>\""
Expand Down Expand Up @@ -183,6 +184,33 @@
"print(f\"Expires at: {embed_token['expires_at']}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Duplicating a map\n",
"\n",
"You can duplicate an existing map to create a copy with optional title and destination."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"duplicated_map = duplicate_map(\n",
" map_id=map_id,\n",
" title=\"Duplicated felt-python map\"\n",
" # project_id=\"project_id_here\" # Optional: specify destination project\n",
" # folder_id=\"folder_id_here\" # Optional: specify destination folder\n",
")\n",
"\n",
"duplicated_map_id = duplicated_map[\"id\"]\n",
"print(f\"Duplicated map created with ID: {duplicated_map_id}\")\n",
"print(f\"Duplicated map URL: {duplicated_map['url']}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand All @@ -196,7 +224,10 @@
"metadata": {},
"outputs": [],
"source": [
"delete_map(map_id)"
"delete_map(map_id)\n",
"\n",
"# Also delete the duplicated map\n",
"delete_map(duplicated_map_id)"
]
}
],
Expand Down
25 changes: 23 additions & 2 deletions tests/layer_groups_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
create_map,
list_layer_groups,
get_layer_group,
update_layer_group,
update_layer_groups,
publish_layer_group,
update_layers,
Expand Down Expand Up @@ -191,7 +192,27 @@ def test_layer_groups_workflow(self):

print("Layer groups updated successfully")

# Step 7: Update layers to assign them to groups
# Step 7: Test individual layer group update
print("Testing individual layer group update...")

individual_update_result = update_layer_group(
map_id=map_id,
layer_group_id=group1_id,
name="Vector Data (Individual Update)",
caption="Updated via individual update function",
ordering_key=10,
visibility_interaction="slider"
)

self.assertIsNotNone(individual_update_result)
self.assertEqual(individual_update_result["name"], "Vector Data (Individual Update)")
self.assertEqual(individual_update_result["caption"], "Updated via individual update function")
self.assertEqual(individual_update_result["ordering_key"], 10)
self.assertEqual(individual_update_result["visibility_interaction"], "slider")

print("Individual layer group update completed successfully")

# Step 8: Update layers to assign them to groups
print("Updating layers to assign them to groups...")
layer_updates = [
{
Expand Down Expand Up @@ -221,7 +242,7 @@ def test_layer_groups_workflow(self):
any(layer["id"] == layer2_id for layer in group2_details["layers"])
)

# Step 8: Publish a layer group to the library
# Step 9: Publish a layer group to the library
print(f"Publishing layer group: {group1_id} to the library...")
try:
published_group = publish_layer_group(
Expand Down
23 changes: 23 additions & 0 deletions tests/maps_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
resolve_comment,
delete_comment,
create_embed_token,
duplicate_map,
)


Expand Down Expand Up @@ -134,6 +135,28 @@ def test_map_workflow(self):
self.assertIn("token", token_data)
self.assertIn("expires_at", token_data)
print(f"Created embed token that expires at {token_data['expires_at']}")

# Step 8: Duplicate the map
print("Duplicating map...")

duplicated_map_name = f"Duplicated Map ({self.timestamp})"
duplicated_map = duplicate_map(
map_id=map_id,
title=duplicated_map_name
)

self.assertIsNotNone(duplicated_map)
self.assertIn("id", duplicated_map)
self.assertEqual(duplicated_map["title"], duplicated_map_name)
self.assertNotEqual(duplicated_map["id"], map_id) # Should be different ID

duplicated_map_id = duplicated_map["id"]
print(f"Duplicated map created with ID: {duplicated_map_id}")

# Clean up duplicated map
print("Cleaning up duplicated map...")
delete_map(duplicated_map_id)

print("\nTest completed successfully!")


Expand Down