%global _empty_manifest_terminate_build 0 Name: python-simple-smartsheet Version: 0.5.0 Release: 1 Summary: Python library to interact with Smartsheet API License: MIT URL: https://github.com/dmfigol/simple-smartsheet Source0: https://mirrors.nju.edu.cn/pypi/web/packages/22/2a/10ea16179fbac3d613dba4b72bfdf9a7e535124f62f45385cc7b762a1699/simple-smartsheet-0.5.0.tar.gz BuildArch: noarch Requires: python3-marshmallow Requires: python3-requests Requires: python3-attrs Requires: python3-cattrs Requires: python3-mypy-extensions Requires: python3-aiohttp Requires: python3-pandas %description ## Simple Smartsheet Python library to interact with Smartsheet API easily ### Installation Requires Python 3.6+ `pip install simple-smartsheet` ### Why not smartsheet-python-sdk `smartsheet-python-sdk` has very wide object coverage and maps to Smartsheet API very nicely, but it does not have some convenience features (for example, easy access to cells by column titles). `simple-smartsheet` library is focused on the user experience in expense of feature coverage. As of now, you can only interact with Sheets and Reports and their children objects (rows, columns, cells). Additionally, `simple-smartsheet` supports asyncio and provides both sync and async API at the same time. ### Usage ```python import os from datetime import date from pprint import pprint from simple_smartsheet import Smartsheet from simple_smartsheet.models import Sheet, Column, Row, Cell, ColumnType TOKEN = os.getenv("SMARTSHEET_API_TOKEN") SHEET_NAME = "[TEST] My New Sheet" smartsheet = Smartsheet(TOKEN) # retrieve a list of sheets (limited set of attributes) sheets = smartsheet.sheets.list() pprint(sheets) # delete the test sheet if already exists for sheet in sheets: if sheet.name == SHEET_NAME: smartsheet.sheets.delete(id=sheet.id) # create a new Sheet new_sheet_skeleton = Sheet( name=SHEET_NAME, columns=[ Column(primary=True, title="Full Name", type=ColumnType.TEXT_NUMBER), Column(title="Number of read books", type=ColumnType.TEXT_NUMBER), Column(title="Birth date", type=ColumnType.DATE), Column(title="Library member", type=ColumnType.CHECKBOX), ], ) # print the sheet object attributes used by the Smartsheet API (camelCase) pprint(new_sheet_skeleton.dump()) # add the sheet via API result = smartsheet.sheets.create(new_sheet_skeleton) sheet = result.obj print(f"ID of the created sheet is {sheet.id!r}") # retrieve a sheet by name # this object is exactly the same as result.obj sheet = smartsheet.sheets.get(SHEET_NAME) # get columns details by column title (case-sensitive) full_name_column = sheet.get_column("Full Name") pprint(full_name_column.__dict__) num_books_column = sheet.get_column("Number of read books") pprint(num_books_column.__dict__) # add rows (cells are created using different ways) # second way is the easiest new_rows = [ Row( to_top=True, cells=[ Cell(column_id=full_name_column.id, value="Alice Smith"), Cell(column_id=num_books_column.id, value=5), ], ), Row( to_top=True, cells=sheet.make_cells( {"Full Name": "Bob Lee", "Number of read books": 2} ), ), Row( to_top=True, cells=[ sheet.make_cell("Full Name", "Charlie Brown"), sheet.make_cell("Number of read books", 1), sheet.make_cell("Birth date", date(1990, 1, 1)), ], ), ] smartsheet.sheets.add_rows(sheet.id, new_rows) # sort rows by column "Full Name" descending / returns updated sheet sheet = smartsheet.sheets.sort_rows( sheet, [{"column_title": "Full Name", "descending": True}] ) print("\nSheet after adding rows:") # print a list of dictionaries containing column titles and values for each row pprint(sheet.as_list()) # get a specific cell and updating it: row_id_to_delete = None rows_to_update = [] for row in sheet.rows: full_name = row.get_cell("Full Name").value num_books = row.get_cell("Number of read books").value print(f"{full_name} has read {num_books} books") if full_name.startswith("Charlie"): updated_row = Row( id=row.id, cells=[sheet.make_cell("Number of read books", 15)] ) rows_to_update.append(updated_row) elif full_name.startswith("Bob"): row_id_to_delete = row.id # used later # update rows smartsheet.sheets.update_rows(sheet.id, rows_to_update) # or a single row # smartsheet.sheets.update_row(sheet.id, rows_to_update[0]) # get an updated sheet sheet = smartsheet.sheets.get(id=sheet.id) print("\nSheet after updating rows:") pprint(sheet.as_list()) # delete a row smartsheet.sheets.delete_row(sheet.id, row_id_to_delete) # get an updated sheet sheet = smartsheet.sheets.get(id=sheet.id) print("\nSheet after deleting rows:") pprint(sheet.as_list()) # delete a sheet by name smartsheet.sheets.delete(SHEET_NAME) sheets = smartsheet.sheets.list() pprint(sheets) ``` ### API reference While a separate docs page is work in progress, available public API is described here #### Class `simple_smartsheet.Smartsheet` This class a main entry point for the library Methods: * `def __init__(token: str)`: constructor for the class Attributes: * `token`: Smartsheet API token, obtained in Personal Settings -> API access * `sheets`: `simple_smartsheet.models.sheet.SheetCRUD` object which provides methods to interact with sheets * `reports`: `simple_smartsheet.models.report.ReportCRUD` object which provides methods to interact with reports #### Class `simple_smartsheet.models.sheet.SheetCRUD` Methods: * `def get(name: Optional[str], id: Optional[int]) -> Sheet`: fetches Sheet by name or ID. * `def list() -> List[Sheet]`: fetches a list of all sheets (summary only) * `def create(obj: Sheet) -> Result`: adds a new sheet * `def update(obj: Sheet) -> Result`: updates a sheet * `def delete(name: Optional[str], id: Optional[int]) -> Result`: deletes a sheet by name or ID * `def add_rows(sheet_id: int, rows: Sequence[Row]) -> Result`: adds rows to the sheet * `def add_row(sheet_id: int, row: Row) -> Result`: add a single row to the sheet * `def update_rows(sheet_id: int, rows: Sequence[Row]) -> Result`: updates several rows in the sheet * `def update_row(sheet_id: int, row: Row) -> Result`: updates a single row * `def delete_rows(sheet_id: int, row_ids: Sequence[int]) -> Result`: deletes several rows with provided ids * `def delete_row(sheet_id: int, row_id: int) -> Result`: deletes a single row with a provided id * `def sort_rows(sheet: Sheet, order: List[Dict[str, Any]]) -> Sheet`: sorts sheet rows with the specified order, e.g.: ``` sheet.sort_rows([ {"column_title": "Birth date", "descending": True}, {"column_title": "Full Name"} ]) ``` #### Class `simple_smartsheet.models.sheet.AsyncSheetCRUD` The methods listed below are asynchronous version of methods in `SheetCRUD`, listed for completeness: * `async def get(name: Optional[str], id: Optional[int]) -> Sheet` * `async def list() -> List[Sheet]` * `async def create(obj: Sheet) -> Result` * `async def update(obj: Sheet) -> Result` * `async def delete(name: Optional[str], id: Optional[int]) -> Result` * `async def add_rows(sheet_id: int, rows: Sequence[Row]) -> Result` * `async def add_row(sheet_id: int, row: Row) -> Result` * `async def update_rows(sheet_id: int, rows: Sequence[Row]) -> Result` * `async def update_row(sheet_id: int, row: Row) -> Result` * `async def delete_rows(sheet_id: int, row_ids: Sequence[int]) -> Result` * `async def delete_row(sheet_id: int, row_id: int) -> Result` * `async def sort_rows(sheet: Sheet, order: List[Dict[str, Any]]) -> Sheet` #### Class `simple_smartsheet.models.Sheet` Attributes (converted from camelCase to snake_case): * Methods: * `def get_row(row_num: Optional[int], row_id: Optional[int], filter: Optional[Dict[str, Any]]) -> Optional[Row]`: returns a Row object by row number, ID or by filter, if a unique index was built (see section "Custom Indexes") * `def get_rows(index_query: Dict[str, Any]) -> List[Row]`: returns list of Row objects by filter, if an index was built (see section "Custom Indexes") * `def get_column(column_title: Optional[str], column_id: Optional[int]) -> Column`: returns a Column object by column title or id * `def build_index(indexes: List[IndexKeysDict]) -> None`: builds one or more indexes for quick row lookup using `get_row` or `get_rows`, e.g.: ``` sheet.build_index([ {"columns": ("Company Name",), "unique": False}, {"columns": ("Company Name", "Full Name"), "unique": True} ]) ``` * `def make_cell(column_title: str, field_value: Union[float, str, datetime, None]) -> Cell`: creates a Cell object with provided column title and an associated value * `def make_cells(fields: Dict[str, Union[float, str, datetime, None]]) -> List[Cell]`: creates a list of Cell objects from an input dictionary where column title is key associated with the field value * `def as_list() -> List[Dict[str, Any]]`: returns a list of dictionaries where column title is key associated with the field value #### Class `simple_smartsheet.models.row.Row` Attributes (converted from camelCase to snake_case): * * `rowNumber` is mapped to `num` Methods: * `def get_cell(column_title: Optional[str], column_id: Optional[int]) -> Cell` - returns a Cell object by column title (case-sensitive) or column id * `def as_dict() -> Dict[str, Any]` - returns a dictionary of column title to cell value mappings #### Class `simple_smartsheet.models.column.Column` Attributes (converted from camelCase to snake_case): * #### Class `simple_smartsheet.models.cell.Cell` Attributes (converted from camelCase to snake_case): * #### Class `simple_smartsheet.models.extra.Result`: Attributes (converted from camelCase to snake_case): * * `result` attribute is renamed to `obj` to avoid confusion of calling `result.result`. `result` attribute is still available via property #### Class `simple_smartsheet.models.Report` Attributes (converted from camelCase to snake_case): * [http://smartsheet-platform.github.io/api-docs/#reports](http://smartsheet-platform.github.io/api-docs/#reports) Implements the following Sheet methods: * `def get_row(row_num: Optional[int], row_id: Optional[int], filter: Optional[Dict[str, Any]]) -> ReportRow`: returns a ReportRow object by row number, ID or by filter, if a unique index was built (see section "Custom Indexes") * `def get_rows(index_query: Dict[str, Any]) -> List[ReportRow]`: returns list of ReportRow objects by filter, if an index was built (see section "Custom Indexes") * `def get_column(column_title: Optional[str], column_id: Optional[int]) -> ReportColumn`: returns a ReportColumn object by column title or id * `def build_index(indexes: List[IndexKeysDict]) -> None`: builds one or more indexes for quick row lookup using `get_row` or `get_rows`, e.g.: ``` sheet.build_index([ {"columns": ("Company Name",), "unique": False}, {"columns": ("Company Name", "Full Name"), "unique": True} ]) ``` * `def as_list() -> List[Dict[str, Any]]`: returns a list of dictionaries where column title is key associated with the field value ### Custom Indexes It is possible to build indexes to enable quick rows lookups for sheets and reports. For this, after retrieving the sheet, call `sheet.build_index` function. It takes only one argument: a list of dictionaries, where every dictionary has two keys `columns` and `unique`. `columns` should contain a tuple with column titles (case sensitive). `unique` controls if the index always points to a single row (value `True`, lookups are done using `get_row` method) or multiple rows (value `False`, lookups are done using `get_rows` method). Below you can find a code snippet (see the full example in `examples/custom_indexes.py`): ```python INDEXES = [ {"columns": ("Company",), "unique": False}, {"columns": ("Company", "Full Name"), "unique": True}, {"columns": ("Email address",), "unique": True}, ] sheet = smartsheet.sheets.get("[TEST] Index Sheet") sheet.build_index(INDEXES) print("\nRow where email address is 'charlie.brown@globex.com':") print(sheet.get_row(filter={"Email Address": "charlie.brown@globex.com"}).as_dict()) # > # {'Company Name': 'Globex', # 'Email Address': 'charlie.brown@globex.com', # 'Full Name': 'Charlie Brown'} print("\nRows where the company name is 'ACME':") print([row.as_dict() for row in sheet.get_rows(filter={"Company Name": "ACME"})]) # > # [{'Company Name': 'ACME', # 'Email Address': 'alice.smith@acme.com', # 'Full Name': 'Alice Smith'}, # {'Company Name': 'ACME', # 'Email Address': 'bob.lee@acme.com', # 'Full Name': 'Bob Lee'}] ``` ### Asyncio The library supports asyncio for all i/o methods, instead of calling: ``` smartsheet = Smartsheet(token) sheet = smartsheets.sheets.get('my-sheet') ``` you need to call asynchronous context manager with an async version of smartsheet class: ``` with AsyncSmartsheet(token) as smartsheet: sheet = await smartsheet.sheets.get('my-sheet') ``` A complete asyncio example with different operations on sheets and reports can be found in `examples/async.py` ### Pandas If pandas is installed (either separately or as extras `pip install simple-smartsheet[pandas]`), sheets and rows can be exported as `pandas.DataFrame` or `pandas.Series` respectively. Besides column titles and respective values from the sheet, they will also include row IDs and row numbers ``` sheet = smartsheets.sheets.get('my-sheet') df = sheet.as_dataframe() series = sheet.rows[0].as_series() ``` %package -n python3-simple-smartsheet Summary: Python library to interact with Smartsheet API Provides: python-simple-smartsheet BuildRequires: python3-devel BuildRequires: python3-setuptools BuildRequires: python3-pip %description -n python3-simple-smartsheet ## Simple Smartsheet Python library to interact with Smartsheet API easily ### Installation Requires Python 3.6+ `pip install simple-smartsheet` ### Why not smartsheet-python-sdk `smartsheet-python-sdk` has very wide object coverage and maps to Smartsheet API very nicely, but it does not have some convenience features (for example, easy access to cells by column titles). `simple-smartsheet` library is focused on the user experience in expense of feature coverage. As of now, you can only interact with Sheets and Reports and their children objects (rows, columns, cells). Additionally, `simple-smartsheet` supports asyncio and provides both sync and async API at the same time. ### Usage ```python import os from datetime import date from pprint import pprint from simple_smartsheet import Smartsheet from simple_smartsheet.models import Sheet, Column, Row, Cell, ColumnType TOKEN = os.getenv("SMARTSHEET_API_TOKEN") SHEET_NAME = "[TEST] My New Sheet" smartsheet = Smartsheet(TOKEN) # retrieve a list of sheets (limited set of attributes) sheets = smartsheet.sheets.list() pprint(sheets) # delete the test sheet if already exists for sheet in sheets: if sheet.name == SHEET_NAME: smartsheet.sheets.delete(id=sheet.id) # create a new Sheet new_sheet_skeleton = Sheet( name=SHEET_NAME, columns=[ Column(primary=True, title="Full Name", type=ColumnType.TEXT_NUMBER), Column(title="Number of read books", type=ColumnType.TEXT_NUMBER), Column(title="Birth date", type=ColumnType.DATE), Column(title="Library member", type=ColumnType.CHECKBOX), ], ) # print the sheet object attributes used by the Smartsheet API (camelCase) pprint(new_sheet_skeleton.dump()) # add the sheet via API result = smartsheet.sheets.create(new_sheet_skeleton) sheet = result.obj print(f"ID of the created sheet is {sheet.id!r}") # retrieve a sheet by name # this object is exactly the same as result.obj sheet = smartsheet.sheets.get(SHEET_NAME) # get columns details by column title (case-sensitive) full_name_column = sheet.get_column("Full Name") pprint(full_name_column.__dict__) num_books_column = sheet.get_column("Number of read books") pprint(num_books_column.__dict__) # add rows (cells are created using different ways) # second way is the easiest new_rows = [ Row( to_top=True, cells=[ Cell(column_id=full_name_column.id, value="Alice Smith"), Cell(column_id=num_books_column.id, value=5), ], ), Row( to_top=True, cells=sheet.make_cells( {"Full Name": "Bob Lee", "Number of read books": 2} ), ), Row( to_top=True, cells=[ sheet.make_cell("Full Name", "Charlie Brown"), sheet.make_cell("Number of read books", 1), sheet.make_cell("Birth date", date(1990, 1, 1)), ], ), ] smartsheet.sheets.add_rows(sheet.id, new_rows) # sort rows by column "Full Name" descending / returns updated sheet sheet = smartsheet.sheets.sort_rows( sheet, [{"column_title": "Full Name", "descending": True}] ) print("\nSheet after adding rows:") # print a list of dictionaries containing column titles and values for each row pprint(sheet.as_list()) # get a specific cell and updating it: row_id_to_delete = None rows_to_update = [] for row in sheet.rows: full_name = row.get_cell("Full Name").value num_books = row.get_cell("Number of read books").value print(f"{full_name} has read {num_books} books") if full_name.startswith("Charlie"): updated_row = Row( id=row.id, cells=[sheet.make_cell("Number of read books", 15)] ) rows_to_update.append(updated_row) elif full_name.startswith("Bob"): row_id_to_delete = row.id # used later # update rows smartsheet.sheets.update_rows(sheet.id, rows_to_update) # or a single row # smartsheet.sheets.update_row(sheet.id, rows_to_update[0]) # get an updated sheet sheet = smartsheet.sheets.get(id=sheet.id) print("\nSheet after updating rows:") pprint(sheet.as_list()) # delete a row smartsheet.sheets.delete_row(sheet.id, row_id_to_delete) # get an updated sheet sheet = smartsheet.sheets.get(id=sheet.id) print("\nSheet after deleting rows:") pprint(sheet.as_list()) # delete a sheet by name smartsheet.sheets.delete(SHEET_NAME) sheets = smartsheet.sheets.list() pprint(sheets) ``` ### API reference While a separate docs page is work in progress, available public API is described here #### Class `simple_smartsheet.Smartsheet` This class a main entry point for the library Methods: * `def __init__(token: str)`: constructor for the class Attributes: * `token`: Smartsheet API token, obtained in Personal Settings -> API access * `sheets`: `simple_smartsheet.models.sheet.SheetCRUD` object which provides methods to interact with sheets * `reports`: `simple_smartsheet.models.report.ReportCRUD` object which provides methods to interact with reports #### Class `simple_smartsheet.models.sheet.SheetCRUD` Methods: * `def get(name: Optional[str], id: Optional[int]) -> Sheet`: fetches Sheet by name or ID. * `def list() -> List[Sheet]`: fetches a list of all sheets (summary only) * `def create(obj: Sheet) -> Result`: adds a new sheet * `def update(obj: Sheet) -> Result`: updates a sheet * `def delete(name: Optional[str], id: Optional[int]) -> Result`: deletes a sheet by name or ID * `def add_rows(sheet_id: int, rows: Sequence[Row]) -> Result`: adds rows to the sheet * `def add_row(sheet_id: int, row: Row) -> Result`: add a single row to the sheet * `def update_rows(sheet_id: int, rows: Sequence[Row]) -> Result`: updates several rows in the sheet * `def update_row(sheet_id: int, row: Row) -> Result`: updates a single row * `def delete_rows(sheet_id: int, row_ids: Sequence[int]) -> Result`: deletes several rows with provided ids * `def delete_row(sheet_id: int, row_id: int) -> Result`: deletes a single row with a provided id * `def sort_rows(sheet: Sheet, order: List[Dict[str, Any]]) -> Sheet`: sorts sheet rows with the specified order, e.g.: ``` sheet.sort_rows([ {"column_title": "Birth date", "descending": True}, {"column_title": "Full Name"} ]) ``` #### Class `simple_smartsheet.models.sheet.AsyncSheetCRUD` The methods listed below are asynchronous version of methods in `SheetCRUD`, listed for completeness: * `async def get(name: Optional[str], id: Optional[int]) -> Sheet` * `async def list() -> List[Sheet]` * `async def create(obj: Sheet) -> Result` * `async def update(obj: Sheet) -> Result` * `async def delete(name: Optional[str], id: Optional[int]) -> Result` * `async def add_rows(sheet_id: int, rows: Sequence[Row]) -> Result` * `async def add_row(sheet_id: int, row: Row) -> Result` * `async def update_rows(sheet_id: int, rows: Sequence[Row]) -> Result` * `async def update_row(sheet_id: int, row: Row) -> Result` * `async def delete_rows(sheet_id: int, row_ids: Sequence[int]) -> Result` * `async def delete_row(sheet_id: int, row_id: int) -> Result` * `async def sort_rows(sheet: Sheet, order: List[Dict[str, Any]]) -> Sheet` #### Class `simple_smartsheet.models.Sheet` Attributes (converted from camelCase to snake_case): * Methods: * `def get_row(row_num: Optional[int], row_id: Optional[int], filter: Optional[Dict[str, Any]]) -> Optional[Row]`: returns a Row object by row number, ID or by filter, if a unique index was built (see section "Custom Indexes") * `def get_rows(index_query: Dict[str, Any]) -> List[Row]`: returns list of Row objects by filter, if an index was built (see section "Custom Indexes") * `def get_column(column_title: Optional[str], column_id: Optional[int]) -> Column`: returns a Column object by column title or id * `def build_index(indexes: List[IndexKeysDict]) -> None`: builds one or more indexes for quick row lookup using `get_row` or `get_rows`, e.g.: ``` sheet.build_index([ {"columns": ("Company Name",), "unique": False}, {"columns": ("Company Name", "Full Name"), "unique": True} ]) ``` * `def make_cell(column_title: str, field_value: Union[float, str, datetime, None]) -> Cell`: creates a Cell object with provided column title and an associated value * `def make_cells(fields: Dict[str, Union[float, str, datetime, None]]) -> List[Cell]`: creates a list of Cell objects from an input dictionary where column title is key associated with the field value * `def as_list() -> List[Dict[str, Any]]`: returns a list of dictionaries where column title is key associated with the field value #### Class `simple_smartsheet.models.row.Row` Attributes (converted from camelCase to snake_case): * * `rowNumber` is mapped to `num` Methods: * `def get_cell(column_title: Optional[str], column_id: Optional[int]) -> Cell` - returns a Cell object by column title (case-sensitive) or column id * `def as_dict() -> Dict[str, Any]` - returns a dictionary of column title to cell value mappings #### Class `simple_smartsheet.models.column.Column` Attributes (converted from camelCase to snake_case): * #### Class `simple_smartsheet.models.cell.Cell` Attributes (converted from camelCase to snake_case): * #### Class `simple_smartsheet.models.extra.Result`: Attributes (converted from camelCase to snake_case): * * `result` attribute is renamed to `obj` to avoid confusion of calling `result.result`. `result` attribute is still available via property #### Class `simple_smartsheet.models.Report` Attributes (converted from camelCase to snake_case): * [http://smartsheet-platform.github.io/api-docs/#reports](http://smartsheet-platform.github.io/api-docs/#reports) Implements the following Sheet methods: * `def get_row(row_num: Optional[int], row_id: Optional[int], filter: Optional[Dict[str, Any]]) -> ReportRow`: returns a ReportRow object by row number, ID or by filter, if a unique index was built (see section "Custom Indexes") * `def get_rows(index_query: Dict[str, Any]) -> List[ReportRow]`: returns list of ReportRow objects by filter, if an index was built (see section "Custom Indexes") * `def get_column(column_title: Optional[str], column_id: Optional[int]) -> ReportColumn`: returns a ReportColumn object by column title or id * `def build_index(indexes: List[IndexKeysDict]) -> None`: builds one or more indexes for quick row lookup using `get_row` or `get_rows`, e.g.: ``` sheet.build_index([ {"columns": ("Company Name",), "unique": False}, {"columns": ("Company Name", "Full Name"), "unique": True} ]) ``` * `def as_list() -> List[Dict[str, Any]]`: returns a list of dictionaries where column title is key associated with the field value ### Custom Indexes It is possible to build indexes to enable quick rows lookups for sheets and reports. For this, after retrieving the sheet, call `sheet.build_index` function. It takes only one argument: a list of dictionaries, where every dictionary has two keys `columns` and `unique`. `columns` should contain a tuple with column titles (case sensitive). `unique` controls if the index always points to a single row (value `True`, lookups are done using `get_row` method) or multiple rows (value `False`, lookups are done using `get_rows` method). Below you can find a code snippet (see the full example in `examples/custom_indexes.py`): ```python INDEXES = [ {"columns": ("Company",), "unique": False}, {"columns": ("Company", "Full Name"), "unique": True}, {"columns": ("Email address",), "unique": True}, ] sheet = smartsheet.sheets.get("[TEST] Index Sheet") sheet.build_index(INDEXES) print("\nRow where email address is 'charlie.brown@globex.com':") print(sheet.get_row(filter={"Email Address": "charlie.brown@globex.com"}).as_dict()) # > # {'Company Name': 'Globex', # 'Email Address': 'charlie.brown@globex.com', # 'Full Name': 'Charlie Brown'} print("\nRows where the company name is 'ACME':") print([row.as_dict() for row in sheet.get_rows(filter={"Company Name": "ACME"})]) # > # [{'Company Name': 'ACME', # 'Email Address': 'alice.smith@acme.com', # 'Full Name': 'Alice Smith'}, # {'Company Name': 'ACME', # 'Email Address': 'bob.lee@acme.com', # 'Full Name': 'Bob Lee'}] ``` ### Asyncio The library supports asyncio for all i/o methods, instead of calling: ``` smartsheet = Smartsheet(token) sheet = smartsheets.sheets.get('my-sheet') ``` you need to call asynchronous context manager with an async version of smartsheet class: ``` with AsyncSmartsheet(token) as smartsheet: sheet = await smartsheet.sheets.get('my-sheet') ``` A complete asyncio example with different operations on sheets and reports can be found in `examples/async.py` ### Pandas If pandas is installed (either separately or as extras `pip install simple-smartsheet[pandas]`), sheets and rows can be exported as `pandas.DataFrame` or `pandas.Series` respectively. Besides column titles and respective values from the sheet, they will also include row IDs and row numbers ``` sheet = smartsheets.sheets.get('my-sheet') df = sheet.as_dataframe() series = sheet.rows[0].as_series() ``` %package help Summary: Development documents and examples for simple-smartsheet Provides: python3-simple-smartsheet-doc %description help ## Simple Smartsheet Python library to interact with Smartsheet API easily ### Installation Requires Python 3.6+ `pip install simple-smartsheet` ### Why not smartsheet-python-sdk `smartsheet-python-sdk` has very wide object coverage and maps to Smartsheet API very nicely, but it does not have some convenience features (for example, easy access to cells by column titles). `simple-smartsheet` library is focused on the user experience in expense of feature coverage. As of now, you can only interact with Sheets and Reports and their children objects (rows, columns, cells). Additionally, `simple-smartsheet` supports asyncio and provides both sync and async API at the same time. ### Usage ```python import os from datetime import date from pprint import pprint from simple_smartsheet import Smartsheet from simple_smartsheet.models import Sheet, Column, Row, Cell, ColumnType TOKEN = os.getenv("SMARTSHEET_API_TOKEN") SHEET_NAME = "[TEST] My New Sheet" smartsheet = Smartsheet(TOKEN) # retrieve a list of sheets (limited set of attributes) sheets = smartsheet.sheets.list() pprint(sheets) # delete the test sheet if already exists for sheet in sheets: if sheet.name == SHEET_NAME: smartsheet.sheets.delete(id=sheet.id) # create a new Sheet new_sheet_skeleton = Sheet( name=SHEET_NAME, columns=[ Column(primary=True, title="Full Name", type=ColumnType.TEXT_NUMBER), Column(title="Number of read books", type=ColumnType.TEXT_NUMBER), Column(title="Birth date", type=ColumnType.DATE), Column(title="Library member", type=ColumnType.CHECKBOX), ], ) # print the sheet object attributes used by the Smartsheet API (camelCase) pprint(new_sheet_skeleton.dump()) # add the sheet via API result = smartsheet.sheets.create(new_sheet_skeleton) sheet = result.obj print(f"ID of the created sheet is {sheet.id!r}") # retrieve a sheet by name # this object is exactly the same as result.obj sheet = smartsheet.sheets.get(SHEET_NAME) # get columns details by column title (case-sensitive) full_name_column = sheet.get_column("Full Name") pprint(full_name_column.__dict__) num_books_column = sheet.get_column("Number of read books") pprint(num_books_column.__dict__) # add rows (cells are created using different ways) # second way is the easiest new_rows = [ Row( to_top=True, cells=[ Cell(column_id=full_name_column.id, value="Alice Smith"), Cell(column_id=num_books_column.id, value=5), ], ), Row( to_top=True, cells=sheet.make_cells( {"Full Name": "Bob Lee", "Number of read books": 2} ), ), Row( to_top=True, cells=[ sheet.make_cell("Full Name", "Charlie Brown"), sheet.make_cell("Number of read books", 1), sheet.make_cell("Birth date", date(1990, 1, 1)), ], ), ] smartsheet.sheets.add_rows(sheet.id, new_rows) # sort rows by column "Full Name" descending / returns updated sheet sheet = smartsheet.sheets.sort_rows( sheet, [{"column_title": "Full Name", "descending": True}] ) print("\nSheet after adding rows:") # print a list of dictionaries containing column titles and values for each row pprint(sheet.as_list()) # get a specific cell and updating it: row_id_to_delete = None rows_to_update = [] for row in sheet.rows: full_name = row.get_cell("Full Name").value num_books = row.get_cell("Number of read books").value print(f"{full_name} has read {num_books} books") if full_name.startswith("Charlie"): updated_row = Row( id=row.id, cells=[sheet.make_cell("Number of read books", 15)] ) rows_to_update.append(updated_row) elif full_name.startswith("Bob"): row_id_to_delete = row.id # used later # update rows smartsheet.sheets.update_rows(sheet.id, rows_to_update) # or a single row # smartsheet.sheets.update_row(sheet.id, rows_to_update[0]) # get an updated sheet sheet = smartsheet.sheets.get(id=sheet.id) print("\nSheet after updating rows:") pprint(sheet.as_list()) # delete a row smartsheet.sheets.delete_row(sheet.id, row_id_to_delete) # get an updated sheet sheet = smartsheet.sheets.get(id=sheet.id) print("\nSheet after deleting rows:") pprint(sheet.as_list()) # delete a sheet by name smartsheet.sheets.delete(SHEET_NAME) sheets = smartsheet.sheets.list() pprint(sheets) ``` ### API reference While a separate docs page is work in progress, available public API is described here #### Class `simple_smartsheet.Smartsheet` This class a main entry point for the library Methods: * `def __init__(token: str)`: constructor for the class Attributes: * `token`: Smartsheet API token, obtained in Personal Settings -> API access * `sheets`: `simple_smartsheet.models.sheet.SheetCRUD` object which provides methods to interact with sheets * `reports`: `simple_smartsheet.models.report.ReportCRUD` object which provides methods to interact with reports #### Class `simple_smartsheet.models.sheet.SheetCRUD` Methods: * `def get(name: Optional[str], id: Optional[int]) -> Sheet`: fetches Sheet by name or ID. * `def list() -> List[Sheet]`: fetches a list of all sheets (summary only) * `def create(obj: Sheet) -> Result`: adds a new sheet * `def update(obj: Sheet) -> Result`: updates a sheet * `def delete(name: Optional[str], id: Optional[int]) -> Result`: deletes a sheet by name or ID * `def add_rows(sheet_id: int, rows: Sequence[Row]) -> Result`: adds rows to the sheet * `def add_row(sheet_id: int, row: Row) -> Result`: add a single row to the sheet * `def update_rows(sheet_id: int, rows: Sequence[Row]) -> Result`: updates several rows in the sheet * `def update_row(sheet_id: int, row: Row) -> Result`: updates a single row * `def delete_rows(sheet_id: int, row_ids: Sequence[int]) -> Result`: deletes several rows with provided ids * `def delete_row(sheet_id: int, row_id: int) -> Result`: deletes a single row with a provided id * `def sort_rows(sheet: Sheet, order: List[Dict[str, Any]]) -> Sheet`: sorts sheet rows with the specified order, e.g.: ``` sheet.sort_rows([ {"column_title": "Birth date", "descending": True}, {"column_title": "Full Name"} ]) ``` #### Class `simple_smartsheet.models.sheet.AsyncSheetCRUD` The methods listed below are asynchronous version of methods in `SheetCRUD`, listed for completeness: * `async def get(name: Optional[str], id: Optional[int]) -> Sheet` * `async def list() -> List[Sheet]` * `async def create(obj: Sheet) -> Result` * `async def update(obj: Sheet) -> Result` * `async def delete(name: Optional[str], id: Optional[int]) -> Result` * `async def add_rows(sheet_id: int, rows: Sequence[Row]) -> Result` * `async def add_row(sheet_id: int, row: Row) -> Result` * `async def update_rows(sheet_id: int, rows: Sequence[Row]) -> Result` * `async def update_row(sheet_id: int, row: Row) -> Result` * `async def delete_rows(sheet_id: int, row_ids: Sequence[int]) -> Result` * `async def delete_row(sheet_id: int, row_id: int) -> Result` * `async def sort_rows(sheet: Sheet, order: List[Dict[str, Any]]) -> Sheet` #### Class `simple_smartsheet.models.Sheet` Attributes (converted from camelCase to snake_case): * Methods: * `def get_row(row_num: Optional[int], row_id: Optional[int], filter: Optional[Dict[str, Any]]) -> Optional[Row]`: returns a Row object by row number, ID or by filter, if a unique index was built (see section "Custom Indexes") * `def get_rows(index_query: Dict[str, Any]) -> List[Row]`: returns list of Row objects by filter, if an index was built (see section "Custom Indexes") * `def get_column(column_title: Optional[str], column_id: Optional[int]) -> Column`: returns a Column object by column title or id * `def build_index(indexes: List[IndexKeysDict]) -> None`: builds one or more indexes for quick row lookup using `get_row` or `get_rows`, e.g.: ``` sheet.build_index([ {"columns": ("Company Name",), "unique": False}, {"columns": ("Company Name", "Full Name"), "unique": True} ]) ``` * `def make_cell(column_title: str, field_value: Union[float, str, datetime, None]) -> Cell`: creates a Cell object with provided column title and an associated value * `def make_cells(fields: Dict[str, Union[float, str, datetime, None]]) -> List[Cell]`: creates a list of Cell objects from an input dictionary where column title is key associated with the field value * `def as_list() -> List[Dict[str, Any]]`: returns a list of dictionaries where column title is key associated with the field value #### Class `simple_smartsheet.models.row.Row` Attributes (converted from camelCase to snake_case): * * `rowNumber` is mapped to `num` Methods: * `def get_cell(column_title: Optional[str], column_id: Optional[int]) -> Cell` - returns a Cell object by column title (case-sensitive) or column id * `def as_dict() -> Dict[str, Any]` - returns a dictionary of column title to cell value mappings #### Class `simple_smartsheet.models.column.Column` Attributes (converted from camelCase to snake_case): * #### Class `simple_smartsheet.models.cell.Cell` Attributes (converted from camelCase to snake_case): * #### Class `simple_smartsheet.models.extra.Result`: Attributes (converted from camelCase to snake_case): * * `result` attribute is renamed to `obj` to avoid confusion of calling `result.result`. `result` attribute is still available via property #### Class `simple_smartsheet.models.Report` Attributes (converted from camelCase to snake_case): * [http://smartsheet-platform.github.io/api-docs/#reports](http://smartsheet-platform.github.io/api-docs/#reports) Implements the following Sheet methods: * `def get_row(row_num: Optional[int], row_id: Optional[int], filter: Optional[Dict[str, Any]]) -> ReportRow`: returns a ReportRow object by row number, ID or by filter, if a unique index was built (see section "Custom Indexes") * `def get_rows(index_query: Dict[str, Any]) -> List[ReportRow]`: returns list of ReportRow objects by filter, if an index was built (see section "Custom Indexes") * `def get_column(column_title: Optional[str], column_id: Optional[int]) -> ReportColumn`: returns a ReportColumn object by column title or id * `def build_index(indexes: List[IndexKeysDict]) -> None`: builds one or more indexes for quick row lookup using `get_row` or `get_rows`, e.g.: ``` sheet.build_index([ {"columns": ("Company Name",), "unique": False}, {"columns": ("Company Name", "Full Name"), "unique": True} ]) ``` * `def as_list() -> List[Dict[str, Any]]`: returns a list of dictionaries where column title is key associated with the field value ### Custom Indexes It is possible to build indexes to enable quick rows lookups for sheets and reports. For this, after retrieving the sheet, call `sheet.build_index` function. It takes only one argument: a list of dictionaries, where every dictionary has two keys `columns` and `unique`. `columns` should contain a tuple with column titles (case sensitive). `unique` controls if the index always points to a single row (value `True`, lookups are done using `get_row` method) or multiple rows (value `False`, lookups are done using `get_rows` method). Below you can find a code snippet (see the full example in `examples/custom_indexes.py`): ```python INDEXES = [ {"columns": ("Company",), "unique": False}, {"columns": ("Company", "Full Name"), "unique": True}, {"columns": ("Email address",), "unique": True}, ] sheet = smartsheet.sheets.get("[TEST] Index Sheet") sheet.build_index(INDEXES) print("\nRow where email address is 'charlie.brown@globex.com':") print(sheet.get_row(filter={"Email Address": "charlie.brown@globex.com"}).as_dict()) # > # {'Company Name': 'Globex', # 'Email Address': 'charlie.brown@globex.com', # 'Full Name': 'Charlie Brown'} print("\nRows where the company name is 'ACME':") print([row.as_dict() for row in sheet.get_rows(filter={"Company Name": "ACME"})]) # > # [{'Company Name': 'ACME', # 'Email Address': 'alice.smith@acme.com', # 'Full Name': 'Alice Smith'}, # {'Company Name': 'ACME', # 'Email Address': 'bob.lee@acme.com', # 'Full Name': 'Bob Lee'}] ``` ### Asyncio The library supports asyncio for all i/o methods, instead of calling: ``` smartsheet = Smartsheet(token) sheet = smartsheets.sheets.get('my-sheet') ``` you need to call asynchronous context manager with an async version of smartsheet class: ``` with AsyncSmartsheet(token) as smartsheet: sheet = await smartsheet.sheets.get('my-sheet') ``` A complete asyncio example with different operations on sheets and reports can be found in `examples/async.py` ### Pandas If pandas is installed (either separately or as extras `pip install simple-smartsheet[pandas]`), sheets and rows can be exported as `pandas.DataFrame` or `pandas.Series` respectively. Besides column titles and respective values from the sheet, they will also include row IDs and row numbers ``` sheet = smartsheets.sheets.get('my-sheet') df = sheet.as_dataframe() series = sheet.rows[0].as_series() ``` %prep %autosetup -n simple-smartsheet-0.5.0 %build %py3_build %install %py3_install install -d -m755 %{buildroot}/%{_pkgdocdir} if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi pushd %{buildroot} if [ -d usr/lib ]; then find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/lib64 ]; then find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/bin ]; then find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/sbin ]; then find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst fi touch doclist.lst if [ -d usr/share/man ]; then find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst fi popd mv %{buildroot}/filelist.lst . mv %{buildroot}/doclist.lst . %files -n python3-simple-smartsheet -f filelist.lst %dir %{python3_sitelib}/* %files help -f doclist.lst %{_docdir}/* %changelog * Wed May 10 2023 Python_Bot - 0.5.0-1 - Package Spec generated