Skip to content
Open
Show file tree
Hide file tree
Changes from 14 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
17 changes: 17 additions & 0 deletions shotgun_api3/shotgun.py
Original file line number Diff line number Diff line change
Expand Up @@ -1826,6 +1826,23 @@ def work_schedule_update(

return self._call_rpc("work_schedule_update", params)

def export_page(self, page_id, format, layout_name=None):
"""
Export the specified page to the given format.
This method allows you to export a page to a specific format such as CSV.
>>> sg.export_page(12345, "csv", layout_name="My Layout")
"ID,Name,Status\n1,Shot 001,ip\n2,Shot 002,rev\n"
:param int page_id: The ID of the page to export.
:param str format: The format to export the page to. Supported formats are ``"csv"``
:param str layout_name: The name of the layout to export this will map to the layout_display_name field. Defaults to ``None``.
:returns: string containing data of the given page.
:rtype: string
"""

params = dict(format=format, page_id=page_id, layout_name=layout_name)

return self._call_rpc("export_page", params)

def follow(self, user, entity):
"""
Add the entity to the user's followed entities.
Expand Down
77 changes: 77 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1947,6 +1947,83 @@ def test_include_archived_projects(self):
self.sg.update("Project", self.project["id"], {"archived": False})


class TestExportPage(base.LiveTestBase):

def setUp(self):
super(TestExportPage, self).setUp("HumanUser")

def test_export_page_unavailable(self):
"""
Test export_page raises when report does not exist.
"""
if not self.sg.server_caps.version or self.sg.server_caps.version < (5, 1, 22):
return

page_entity = self.sg.create("Page", {"entity_type": "Shot"})

with self.assertRaises(Exception) as cm:
self.sg.export_page(page_entity["id"], "csv")
self.assertIn(
f"Export for Page id={page_entity['id']} not available", str(cm.exception)
)

with self.assertRaises(Exception) as cm:
self.sg.export_page(page_entity["id"], "csv", layout_name="My Layout")
self.assertIn(
f"Export for Page id={page_entity['id']} not available", str(cm.exception)
)

def test_export_page_format_missing(self):
"""
Test export_page raises for invalid format.
"""
if not self.sg.server_caps.version or self.sg.server_caps.version < (5, 1, 22):
return

with self.assertRaises(Exception) as cm:
self.sg.export_page(11, None)
self.assertIn("'format' missing", str(cm.exception))

with self.assertRaises(Exception) as cm:
self.sg.export_page(11, None, layout_name="My Layout")
self.assertIn("'format' missing", str(cm.exception))

def test_export_page_missing_page_id(self):
"""
Test export_page raises for missing page id.
"""
if not self.sg.server_caps.version or self.sg.server_caps.version < (5, 1, 22):
return

with self.assertRaises(Exception) as cm:
self.sg.export_page(None, "csv")
self.assertIn("'page_id' missing", str(cm.exception))

with self.assertRaises(Exception) as cm:
self.sg.export_page(None, "csv", layout_name="My Layout")
self.assertIn("'page_id' missing", str(cm.exception))

@unittest.mock.patch("shotgun_api3.shotgun.Http.request")
def test_export_page_without_layout_name(self, mock_request):
Copy link
Author

@kuldeepgudekar kuldeepgudekar Sep 16, 2025

Choose a reason for hiding this comment

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

We have not exposed the ExportPage entity so that we can't create it from self.sg, we can only query it. We can create it from FPTR ui calls.

"""
Test export_page works when layout_name is not provided.
"""

if not self.sg.server_caps.version or self.sg.server_caps.version < (5, 1, 22):
return

# Mock the underlying Http.request to return CSV content with appropriate headers
csv_body = "ID,Name,Status\n1,Shot 001,ip\n2,Shot 002,rev\n"
response = unittest.mock.MagicMock(name="response mock")
response.status = 200
response.reason = "OK"
response.items.return_value = [("content-type", "text/csv; charset=utf-8")]
mock_request.return_value = (response, csv_body)
result = self.sg.export_page(11, "csv")
self.assertIsInstance(result, str)
self.assertTrue(result.startswith("ID,Name,Status"))


class TestFollow(base.LiveTestBase):

def test_follow_unfollow(self):
Expand Down