Package: smartsheet-pydantic
smartsheet-pydanticWhat is it?Smartsheet Python SDK wrapper, incorporating Pydantic models for type validation to guarantee successful data read/updates into Smartsheet.Smartsheet API requires the incoming data to comply with Smartsheet column types. When the types do not align then the write call can fail. In order streamline the data validation this package piggybacks off the Pydantic BaseModel structure and their data validation capabilities as a "SmartModel".This package also provides frameworks to create a "DataSource" class which you can declaratively define your data source and attach it to the SmartModel, so you can have a standard method of querying your datasource and create/update into Smartsheet.Getting StartedInstall & Import The Modulerequires python greater than 3.10Standard installation using pip install, and package import.pip install smartsheet-pydantic# The package name uses underscore "_" instead of hyphenimportsmartsheet_pydanticEverything revolves around theControllersmartsheet_pydanticpackage's function revolves around theController. Once a Controller is setup, it can facilitate data extraction from the user designated data source (RESTful API or PostgreSQL) and refresh the designated Smartsheet using an additive approach (data is updated or added, but never deleted). TheControllercan also facilitate writing into the Smartsheet.During both the write, read, or update of the Smartsheet thesmartsheet_pydanticas the name suggest usesPydantic's BaseModel to validate the data. This ensures the data has the write data type and will not fail to write to the given Smartsheet.Defining the ControllerIn order to generate the Controller we will use theSheetDetailclass to collect the necessary details.sheet_id: Every Smartsheet grid has a unique id, this is used to target the Smartsheet.description: User defined description of the target Smartsheet to give some context. Can be anything.smart_model: Asmartsheet-pydanticmodel we will create to aid in data validation (Extension of Pydantic BaseModel).source: default None. This field is used to override a Smartmodels default DataSource for unit testing purposes. Omit for normal use.fromsmartsheet_pydantic.controllerimportSheetDetailfromsmartsheet_pydantic.smartmodelsimportSmartModelfromsmartsheet_pydantic.sourcesimportDataSourcesheet_detail=SheetDetail(sheet_id:int=0123456789description:str="Description of the target Smartsheet"smart_model:SmartModel=WeatherModel# we will create this in the subsequent steps)Defining a DataSourceThere are options when creating a data source. One is a RESTful API endpoint, where the you can extend theDataSourceAPIclass, and provide aurlattribute. The other is a PostgreSQL database source usingDataSourcePostgres, where you can provide the access details as well as the query to use, and the column name designations. We will use the latter in this example.fromsmartsheet_pydantic.sourcesimportDataSourceAPI,DataSourcePostgres# DataSource class to call a RESTful API endpoint.classWeatherRestAPI(DataSourceAPI):url='http://127.0.0.1:8000/weather_data'# DataSource class for PostgreSQL databaseclassExamplePostgres(DataSourcePostgres):user:str="username"password:str="password"host:str="host name"database:str="database name"query:str='SELECT * FROM weather_table WHERE location = "USA"'columns:list[str]=["index","date","temperature","humidity","rain"]Defining a SmartModelASmartModelclass is an extension of Pydantic BaseModel. Therefore the definition of aSmartModelis very similar. Define the data fields and their type as a class attribute.You will also need to define 2 additional Configuration parameters.source: this is the DataSource class you defined in the previous step which is associated with this SmartModel.unique_key: is a list of column names which the model will use to define uniqueness. When data in Smartsheet is updated these columns will be used to find and update the data if the data already exists.key_mapping: if you have defined the data field names that differs from the source data, then this dictionary can be used to map and rename those columns. If the data field names are the same, the value must be set to None.fromsmartsheet_pydantic.smartmodelsimportSmartModelfromdatetimeimportdateclassWeatherModel(SmartModel):index:intdate:datetemperature:floathumidity:floatrain:boolclassConfigurationsource=WeatherRestAPIunique_columns:list[int]=['index']key_mapping:dict=NoneGenerating the controllerNow that all of the components are ready we will now take theSheetDetailinstance, and provide it to theSmartsheetControllerFactoryclass to generate aControllerFactory. By calling on the.get_controller()method we can generate theSmarsheetControllerobject.fromsmartsheet_pydantic.controllerimportSmartsheetController,SmartsheetControllerFactorycontroller_factory=SmartsheetControllerFactory()controller:SmartsheetController=controller_factory.get_controller(sheet_detail)Using the controllerUse the provided data source to refresh the target Smartsheetcontroller.refresh_smartsheet_from_source()Extracting data from Smartsheet into a list of SmartModel instances.data:list[WeatherData]=controller.extract_as_smartmodel()Manually write new data, or update existing data to Smartsheetdata:list[WeatherData]controller.update_rows(data)Delete all row data from Smartsheetcontroller.delete_all_rows()SmartModelNot all data can be handled within the SmartModel. You may want to manipulate the data using Pandas or Numpy. Therefore SmartModel has methods to aid in the transforming of data into a SmartModel, as well as extracting data out into a dictionary.Extracting Data From SmartModelweather_data:list[WeatherData]extracted_data:list[dict]=[smart_model.dict()forsmart_modelinweather_data]print(extracted_data)Results[{'index':1'date':date(2023,1,1)'temperature':65.2'humidity':14.5'rain':False},{'index':2'date':date(2023,1,2)'temperature':67.2'humidity':14.2'rain':False},{'index':1'date':date(2023,1,3)'temperature':62.3'humidity':12.2'rain':False},]Converting Data Into SmartModelextracted_data:list[dict]weather_data:list[WeatherData]=\[WeatherData.from_source(data)fordatainextracted_data]