Package: zohocrmsdk2-1
LicenseCopyright (c) 2021, ZOHO CORPORATION PRIVATE LIMITED 
All rights reserved. 

Licensed under the Apache License, Version 2.0 (the "License"); 
you may not use this file except in compliance with the License. 
You may obtain a copy of the License at 

    http://www.apache.org/licenses/LICENSE-2.0 

Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an "AS IS" BASIS, 
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
See the License for the specific language governing permissions and 
limitations under the License.ZOHO CRM PYTHON SDK 2.1 for API version 2.1Table Of ContentsOverviewRegistering a Zoho ClientEnvironmental SetupIncluding the SDK in your projectPersistenceDataBase PersistenceFile PersistenceCustom PersistenceConfigurationInitializationClass HierarchyResponses And ExceptionsThreadingMultithreading in a Multi-User AppMulti-threading in a Single User AppSample CodeOverviewZoho CRM PYTHON SDK offers a way to create client Python applications that can be integrated with Zoho CRM.Registering a Zoho ClientSince Zoho CRM APIs are authenticated with OAuth2 standards, you should register your client app with Zoho. To register your app:Visit this pagehttps://api-console.zoho.comClick onADD CLIENT.Choose theClient Type.EnterClient Name,Client DomainorHomepage URLandAuthorized Redirect URIsthen clickCREATE.Your Client app will be created.Select the created OAuth client.Generate grant token by providing the necessary scopes, time duration (the duration for which the generated token is valid) and Scope Description.Environmental SetupPython SDK is installable throughpip.pipis a tool for dependency management in Python. SDK expects the following from the client app.Client app must have Python(version 3 and above)Python SDK must be installed into client app throughpip.Including the SDK in your projectInstallPythonfrompython.org(if not installed).InstallPython SDKNavigate to the workspace of your client app.Run the command below:pipinstallzohocrmsdk2_1==1.x.xThe Python SDK will be installed in your client application.Token PersistenceToken persistence refers to storing and utilizing the authentication tokens that are provided by Zoho. There are three ways provided by the SDK in which persistence can be utilized. They are DataBase Persistence, File Persistence and Custom Persistence.Table of ContentsDataBase PersistenceFile PersistenceCustom PersistenceImplementing OAuth PersistenceOnce the application is authorized, OAuth access and refresh tokens can be used for subsequent user data requests to Zoho CRM. Hence, they need to be persisted by the client app.The persistence is achieved by writing an implementation of the inbuilt Abstract Base ClassTokenStore, which has the following callback methods.get_token(self, user,token)- invoked before firing a request to fetch the saved tokens. This method should return implementation of inbuiltToken Classobject for the library to process it.save_token(self, user,token)- invoked after fetching access and refresh tokens from Zoho.delete_token(self,token)- invoked before saving the latest tokens.get_tokens(self)- The method to get the all the stored tokens.delete_tokens(self)- The method to delete all the stored tokens.get_token_by_id(self, id, token)- The method to retrieve the user's token details based on unique ID.Note:user is an instance of UserSignature Class.token is an instance of Token Class.DataBase PersistenceIn case the user prefers to use default DataBase persistence,MySQLcan be used.The database name should bezohooauth.There must be a table nameoauthtokenwith the following columns.id int(11)user_mail varchar(255)client_id varchar(255)client_secret varchar(255)refresh_token varchar(255)access_token varchar(255)grant_token varchar(255)expiry_time varchar(20)redirect_url varchar(255)Note:Custom database name and table name can be set in DBStore instance.MySQL QueryCREATETABLEoauthtoken(idvarchar(255)NOTNULL,user_mailvarchar(255)NOTNULL,client_idvarchar(255),client_secretvarchar(255),refresh_tokenvarchar(255),access_tokenvarchar(255),grant_tokenvarchar(255),expiry_timevarchar(20),redirect_urlvarchar(255),primarykey(id))Create DBStore objectfromzcrmsdk.src.com.zoho.api.authenticator.storeimportDBStore"""DBStore takes the following parameters1 -> DataBase host name. Default value "localhost"2 -> DataBase name. Default value "zohooauth"3 -> DataBase user name. Default value "root"4 -> DataBase password. Default value ""5 -> DataBase port number. Default value "3306"6->  DataBase table name . Default value "oauthtoken""""store=DBStore()store=DBStore(host='host_name',database_name='database_name',user_name='user_name',password='password',port_number='port_number',table_name="table_name")File PersistenceIn case of File Persistence, the user can persist tokens in the local drive, by providing the absolute file path to the FileStore object.The File containsiduser_mailclient_idclient_secretrefresh_tokenaccess_tokengrant_tokenexpiry_timeredirect_urlCreate FileStore objectfromzcrmsdk.src.com.zoho.api.authenticator.storeimportFileStore"""FileStore takes the following parameter1 -> Absolute file path of the file to persist tokens"""store=FileStore(file_path='/Users/username/Documents/python_sdk_token.txt')Custom PersistenceTo use Custom Persistence, you must implementTokenStoreand override the methods.fromzcrmsdk.src.com.zoho.api.authenticator.storeimportTokenStoreclassCustomStore(TokenStore):def__init__(self):passdefget_token(self,user,token):"""Parameters:user (UserSignature) : A UserSignature class instance.token (Token) : A Token (zcrmsdk.src.com.zoho.api.authenticator.OAuthToken) class instance"""# Add code to get the tokenreturnNonedefsave_token(self,user,token):"""Parameters:user (UserSignature) : A UserSignature class instance.token (Token) : A Token (zcrmsdk.src.com.zoho.api.authenticator.OAuthToken) class instance"""# Add code to save the tokendefdelete_token(self,token):"""Parameters:token (Token) : A Token (zcrmsdk.src.com.zoho.api.authenticator.OAuthToken) class instance"""# Add code to delete the tokendefget_tokens(self):"""Returns:list: List of stored tokens"""# Add code to get all the stored tokensdefdelete_tokens(self):# Add code to delete all the stored tokensdefget_token_by_id(id,token):"""The method to get id token details.Parameters:id (String) : A String id.token (Token) : A Token class instance.Returns:Token : A Token class instance representing the id token details."""ConfigurationBefore you get started with creating your Python application, you need to register your client and authenticate the app with Zoho.Mandatory KeysOptional Keysuserloggerenvironmentstoretokensdk_configproxyresource_pathCreate an instance ofUserSignatureClass that identifies the current user.fromzcrmsdk.src.com.zoho.crm.api.user_signatureimportUserSignature# Create an UserSignature instance that takes user Email as parameteruser=UserSignature(email='abc@zoho.com')Configure the API environment which decides the domain and the URL to make API calls.fromzcrmsdk.src.com.zoho.crm.api.dcimportUSDataCenter"""Configure the environmentwhich is of the pattern Domain.EnvironmentAvailable Domains: USDataCenter, EUDataCenter, INDataCenter, CNDataCenter, AUDataCenterAvailable Environments: PRODUCTION(), DEVELOPER(), SANDBOX()"""environment=USDataCenter.PRODUCTION()Create an instance ofOAuthTokenwith the information that you get after registering your Zoho client.fromzcrmsdk.src.com.zoho.api.authenticator.oauth_tokenimportOAuthToken"""Create a Token instance that takes the following parameters1 -> OAuth client id.2 -> OAuth client secret.3 -> Grant token.4 -> Refresh token.5 -> OAuth redirect URL. Default value is None6 -> id7 -> Access token"""token=OAuthToken(client_id='clientId',client_secret='clientSecret',grant_token='grant_token',refresh_token="refresh_token",redirect_url='redirectURL',id="id",access_token="access_token")Create an instance ofLoggerClass to log exception and API information. By default, the SDK constructs a Logger instance with level - INFO and file_path - (sdk_logs.log, created in the current working directory)fromzcrmsdk.src.com.zoho.api.loggerimportLogger"""Create an instance of Logger Class that takes two parameters1 -> Level of the log messages to be logged. Can be configured by typing Logger.Levels "." and choose any level from the list displayed.2 -> Absolute file path, where messages need to be logged."""logger=Logger.get_instance(level=Logger.Levels.INFO,file_path="/Users/user_name/Documents/python_sdk_log.log")Create an instance ofTokenStoreto persist tokens, used for authenticating all the requests. By default, the SDK creates the sdk_tokens.txt created in the current working directory) to persist the tokens.fromzcrmsdk.src.com.zoho.api.authenticator.storeimportDBStore,FileStore"""DBStore takes the following parameters1 -> DataBase host name. Default value "localhost"2 -> DataBase name. Default value "zohooauth"3 -> DataBase user name. Default value "root"4 -> DataBase password. Default value ""5 -> DataBase port number. Default value "3306"6 -> DataBase table name. Default value "oauthtoken""""store=DBStore()#store = DBStore(host='host_name', database_name='database_name', user_name='user_name', password='password', port_number='port_number', table_name = "table_name")"""FileStore takes the following parameter1 -> Absolute file path of the file to persist tokens"""#store = FileStore(file_path='/Users/username/Documents/python_sdk_tokens.txt')Create an instance of SDKConfig containing SDK configurations.fromzcrmsdk.src.com.zoho.crm.api.sdk_configimportSDKConfig"""By default, the SDK creates the SDKConfig instanceauto_refresh_fields (Default value is False)if True - all the modules' fields will be auto-refreshed in the background, every hour.if False - the fields will not be auto-refreshed in the background. The user can manually delete the file(s) or refresh the fields using methods from ModuleFieldsHandler(zcrmsdk/src/com/zoho/crm/api/util/module_fields_handler.py)pick_list_validation (Default value is True)A boolean field that validates user input for a pick list field and allows or disallows the addition of a new value to the list.if True - the SDK validates the input. If the value does not exist in the pick list, the SDK throws an error.if False - the SDK does not validate the input and makes the API request with the user’s input to the pick listconnect_timeout (Default value is None)A  Float field to set connect timeoutread_timeout (Default value is None)A  Float field to set read timeout"""config=SDKConfig(auto_refresh_fields=True,pick_list_validation=False,connect_timeout=None,read_timeout=None)The path containing the absolute directory path to store user specific files containing module fields information. By default, the SDK stores the user-specific files within a folder in the current working directory.resource_path='/Users/user_name/Documents/python-app'Create an instance of RequestProxy containing the proxy properties of the user.fromzcrmsdk.src.com.zoho.crm.api.request_proxyimportRequestProxy"""RequestProxy takes the following parameters1 -> Host2 -> Port Number3 -> User Name. Default value is None4 -> Password. Default value is an empty string"""request_proxy=RequestProxy(host='proxyHost',port=80)request_proxy=RequestProxy(host='proxyHost',port=80,user='userName',password='password')Initializing the ApplicationInitialize the SDK using the following code.fromzcrmsdk.src.com.zoho.crm.api.user_signatureimportUserSignaturefromzcrmsdk.src.com.zoho.crm.api.dcimportUSDataCenterfromzcrmsdk.src.com.zoho.api.authenticator.storeimportDBStore,FileStorefromzcrmsdk.src.com.zoho.api.loggerimportLoggerfromzcrmsdk.src.com.zoho.crm.api.initializerimportInitializerfromzcrmsdk.src.com.zoho.api.authenticator.oauth_tokenimportOAuthTokenfromzcrmsdk.src.com.zoho.crm.api.sdk_configimportSDKConfigclassSDKInitializer(object):@staticmethoddefinitialize():"""Create an instance of Logger Class that takes two parameters1 -> Level of the log messages to be logged. Can be configured by typing Logger.Levels "." and choose any level from the list displayed.2 -> Absolute file path, where messages need to be logged."""logger=Logger.get_instance(level=Logger.Levels.INFO,file_path='/Users/user_name/Documents/python_sdk_log.log')# Create an UserSignature instance that takes user Email as parameteruser=UserSignature(email='abc@zoho.com')"""Configure the environmentwhich is of the pattern Domain.EnvironmentAvailable Domains: USDataCenter, EUDataCenter, INDataCenter, CNDataCenter, AUDataCenterAvailable Environments: PRODUCTION(), DEVELOPER(), SANDBOX()"""environment=USDataCenter.PRODUCTION()"""Create a Token instance that takes the following parameters1 -> OAuth client id.2 -> OAuth client secret.3 -> Grant token.4 -> Refresh token.5 -> OAuth redirect URL.6 -> id"""token=OAuthToken(client_id='clientId',client_secret='clientSecret',grant_token='grant_token',refresh_token="refresh_token",redirect_url='redirectURL',id="id")"""Create an instance of TokenStore1 -> Absolute file path of the file to persist tokens"""store=FileStore(file_path='/Users/username/Documents/python_sdk_tokens.txt')"""Create an instance of TokenStore1 -> DataBase host name. Default value "localhost"2 -> DataBase name. Default value "zohooauth"3 -> DataBase user name. Default value "root"4 -> DataBase password. Default value ""5 -> DataBase port number. Default value "3306"6->  DataBase table name . Default value "oauthtoken""""store=DBStore()store=DBStore(host='host_name',database_name='database_name',user_name='user_name',password='password',port_number='port_number',table_name="table_name")"""auto_refresh_fields (Default value is False)if True - all the modules' fields will be auto-refreshed in the background, every hour.if False - the fields will not be auto-refreshed in the background. The user can manually delete the file(s) or refresh the fields using methods from ModuleFieldsHandler(zcrmsdk/src/com/zoho/crm/api/util/module_fields_handler.py)pick_list_validation (Default value is True)A boolean field that validates user input for a pick list field and allows or disallows the addition of a new value to the list.if True - the SDK validates the input. If the value does not exist in the pick list, the SDK throws an error.if False - the SDK does not validate the input and makes the API request with the user’s input to the pick listconnect_timeout (Default value is None)A  Float field to set connect timeoutread_timeout (Default value is None)A  Float field to set read timeout"""config=SDKConfig(auto_refresh_fields=True,pick_list_validation=False,connect_timeout=None,read_timeout=None)"""The path containing the absolute directory path (in the key resource_path) to store user-specific files containing information about fields in modules."""resource_path='/Users/user_name/Documents/python-app'"""Create an instance of RequestProxy class that takes the following parameters1 -> Host2 -> Port Number3 -> User Name. Default value is None4 -> Password. Default value is None"""request_proxy=RequestProxy(host='host',port=8080)request_proxy=RequestProxy(host='host',port=8080,user='user',password='password')"""Call the static initialize method of Initializer class that takes the following arguments1 -> UserSignature instance2 -> Environment instance3 -> Token instance4 -> TokenStore instance5 -> SDKConfig instance6 -> resource_path7 -> Logger instance. Default value is None8 -> RequestProxy instance. Default value is None"""Initializer.initialize(user=user,environment=environment,token=token,store=store,sdk_config=config,resource_path=resource_path,logger=logger,proxy=request_proxy)SDKInitializer.initialize()You can now access the functionalities of the SDK. Refer to the sample codes to make various API calls through the SDK.Class HierarchyResponses and ExceptionsAll SDK methods return an instance of the APIResponse class.After a successful API request, theget_object()method returns an instance of theResponseWrapper(forGET) or theActionWrapper(forPOST, PUT, DELETE)Whenever the API returns an error response, theget_object()returns an instance ofAPIExceptionclass.ResponseWrapper(forGETrequests) andActionWrapper(forPOST, PUT, DELETErequests) are the expected objects for Zoho CRM APIs’ responsesHowever, some specific operations have different expected objects, such as the following:Operations involving records in TagsRecordActionWrapperGetting Record Count for a specific Tag operationCountWrapperOperations involving BaseCurrencyBaseCurrencyActionWrapperLead convert operationConvertActionWrapperRetrieving Deleted records operationDeletedRecordsWrapperRecord image download operationFileBodyWrapperMassUpdate record operationsMassUpdateActionWrapperMassUpdateResponseWrapperGET RequestsTheget_object()returns an instance of one of the following classes, based on the return type.Forapplication/jsonresponsesResponseWrapperCountWrapperDeletedRecordsWrapperMassUpdateResponseWrapperAPIExceptionForfile downloadresponsesFileBodyWrapperAPIExceptionPOST, PUT, DELETE RequestsThegetObject()returns an instance of one of the following classesActionWrapperRecordActionWrapperBaseCurrencyActionWrapperMassUpdateActionWrapperConvertActionWrapperAPIExceptionThese wrapper classes may contain one or a list of instances of the following classes, depending on the response.SuccessResponse Class, if the request was successful.APIException Class, if the request was erroneous.For example, when you insert two records, and one of them was inserted successfully while the other one failed, the ActionWrapper will contain one instance each of the SuccessResponse and APIException classes.All other exceptions such as SDK anomalies and other unexpected behaviours are thrown under the SDKException class.Threading in the Python SDKThreads in a Python program help you achieve parallelism. By using multiple threads, you can make a Python program run faster and do multiple things simultaneously.ThePython SDK(from version 3.x.x) supports both single-user and multi-user app.Multithreading in a Multi-user AppMulti-threading for multi-users is achieved using Initializer's staticswitch_user()method.
switch_user() takes the value initialized previously for user, enviroment, token and sdk_config incase None is passed (or default value is passed). In case of request_proxy, if intended, the value has to be passed again else None(default value) will be taken.# without proxyInitializer.switch_user(user=user,environment=environment,token=token,sdk_config=sdk_config_instance)# with proxyInitializer.switch_user(user=user,environment=environment,token=token,sdk_config=sdk_config_instance,proxy=request_proxy)Here is a sample code to depict multi-threading for a multi-user app.importthreadingfromzcrmsdk.src.com.zoho.crm.api.user_signatureimportUserSignaturefromzcrmsdk.src.com.zoho.crm.api.dcimportUSDataCenter,EUDataCenterfromzcrmsdk.src.com.zoho.api.authenticator.storeimportDBStorefromzcrmsdk.src.com.zoho.api.loggerimportLoggerfromzcrmsdk.src.com.zoho.crm.api.initializerimportInitializerfromzcrmsdk.src.com.zoho.api.authenticator.oauth_tokenimportOAuthTokenfromzcrmsdk.src.com.zoho.crm.api.recordimport*fromzcrmsdk.src.com.zoho.crm.api.request_proxyimportRequestProxyfromzcrmsdk.src.com.zoho.crm.api.sdk_configimportSDKConfigclassMultiThread(threading.Thread):def__init__(self,environment,token,user,module_api_name,sdk_config,proxy=None):super().__init__()self.environment=environmentself.token=tokenself.user=userself.module_api_name=module_api_nameself.sdk_config=sdk_configself.proxy=proxydefrun(self):try:Initializer.switch_user(user=self.user,environment=self.environment,token=self.token,sdk_config=self.sdk_config,proxy=self.proxy)print('Getting records for User: '+Initializer.get_initializer().user.get_email())response=RecordOperations().get_records(self.module_api_name)ifresponseisnotNone:# Get the status code from responseprint('Status Code: '+str(response.get_status_code()))ifresponse.get_status_code()in[204,304]:print('No Content'ifresponse.get_status_code()==204else'Not Modified')return# Get object from responseresponse_object=response.get_object()ifresponse_objectisnotNone:# Check if expected ResponseWrapper instance is received.ifisinstance(response_object,ResponseWrapper):# Get the list of obtained Record instancesrecord_list=response_object.get_data()forrecordinrecord_list:forkey,valueinrecord.get_key_values().items():print(key+" : "+str(value))# Check if the request returned an exceptionelifisinstance(response_object,APIException):# Get the Statusprint("Status: "+response_object.get_status().get_value())# Get the Codeprint("Code: "+response_object.get_code().get_value())print("Details")# Get the details dictdetails=response_object.get_details()forkey,valueindetails.items():print(key+' : '+str(value))# Get the Messageprint("Message: "+response_object.get_message().get_value())exceptExceptionase:print(e)@staticmethoddefcall():logger=Logger.get_instance(level=Logger.Levels.INFO,file_path="/Users/user_name/Documents/python_sdk_log.log")user1=UserSignature(email="abc@zoho.com")token1=OAuthToken(client_id="clientId1",client_secret="clientSecret1",grant_token="Grant Token",refresh_token="refresh_token",id="id")environment1=USDataCenter.PRODUCTION()store=DBStore()sdk_config_1=SDKConfig(auto_refresh_fields=True,pick_list_validation=False)resource_path='/Users/user_name/Documents/python-app'user1_module_api_name='Leads'user2_module_api_name='Contacts'environment2=EUDataCenter.SANDBOX()user2=UserSignature(email="abc@zoho.eu")sdk_config_2=SDKConfig(auto_refresh_fields=False,pick_list_validation=True)token2=OAuthToken(client_id="clientId2",client_secret="clientSecret2",grant_token="GRANT Token",refresh_token="refresh_token",redirect_url="redirectURL",id="id")request_proxy_user_2=RequestProxy("host",8080)Initializer.initialize(user=user1,environment=environment1,token=token1,store=store,sdk_config=sdk_config_1,resource_path=resource_path,logger=logger)t1=MultiThread(environment1,token1,user1,user1_module_api_name,sdk_config_1)t2=MultiThread(environment2,token2,user2,user2_module_api_name,sdk_config_2,request_proxy_user_2)t1.start()t2.start()t1.join()t2.join()MultiThread.call()The program execution starts fromcall().The details ofuser1are given in the variables user1, token1, environment1.Similarly, the details of another useruser2are given in the variables user2, token2, environment2.For each user, an instance ofMultiThread classis created.When thestart()is called which in-turn invokes therun(),  the details of user1 are passed to theswitch_usermethod through theMultiThread object. Therefore, this creates a thread for user1.Similarly, When thestart()is invoked again,  the details of user2 are passed to theswitch_userfunction through theMultiThread object. Therefore, this creates a thread for user2.Multi-threading in a Single User AppHere is a sample code to depict multi-threading for a single-user app.importthreadingfromzcrmsdk.src.com.zoho.crm.api.user_signatureimportUserSignaturefromzcrmsdk.src.com.zoho.crm.api.dcimportUSDataCenterfromzcrmsdk.src.com.zoho.api.authenticator.storeimportDBStorefromzcrmsdk.src.com.zoho.api.loggerimportLoggerfromzcrmsdk.src.com.zoho.crm.api.initializerimportInitializerfromzcrmsdk.src.com.zoho.api.authenticator.oauth_tokenimportOAuthTokenfromzcrmsdk.src.com.zoho.crm.api.sdk_configimportSDKConfigfromzcrmsdk.src.com.zoho.crm.api.recordimport*classMultiThread(threading.Thread):def__init__(self,module_api_name):super().__init__()self.module_api_name=module_api_namedefrun(self):try:print("Calling Get Records for module: "+self.module_api_name)response=RecordOperations().get_records(self.module_api_name)ifresponseisnotNone:# Get the status code from responseprint('Status Code: '+str(response.get_status_code()))ifresponse.get_status_code()in[204,304]:print('No Content'ifresponse.get_status_code()==204else'Not Modified')return# Get object from responseresponse_object=response.get_object()ifresponse_objectisnotNone:# Check if expected ResponseWrapper instance is received.ifisinstance(response_object,ResponseWrapper):# Get the list of obtained Record instancesrecord_list=response_object.get_data()forrecordinrecord_list:forkey,valueinrecord.get_key_values().items():print(key+" : "+str(value))# Check if the request returned an exceptionelifisinstance(response_object,APIException):# Get the Statusprint("Status: "+response_object.get_status().get_value())# Get the Codeprint("Code: "+response_object.get_code().get_value())print("Details")# Get the details dictdetails=response_object.get_details()forkey,valueindetails.items():print(key+' : '+str(value))# Get the Messageprint("Message: "+response_object.get_message().get_value())exceptExceptionase:print(e)@staticmethoddefcall():logger=Logger.get_instance(level=Logger.Levels.INFO,file_path="/Users/user_name/Documents/python_sdk_log.log")user=UserSignature(email="abc@zoho.com")token=OAuthToken(client_id="clientId",client_secret="clientSecret",grant_token="grant_token",refresh_token="refresh_token",redirect_url="redirectURL",id="id")environment=USDataCenter.PRODUCTION()store=DBStore()sdk_config=SDKConfig()resource_path='/Users/user_name/Documents/python-app'Initializer.initialize(user=user,environment=environment,token=token,store=store,sdk_config=sdk_config,resource_path=resource_path,logger=logger)t1=MultiThread('Leads')t2=MultiThread('Quotes')t1.start()t2.start()t1.join()t2.join()MultiThread.call()The program execution starts fromcall()where the SDK is initialized with the details of the user.When thestart()is called which in-turn invokes the run(),  the module_api_name is switched through the MultiThread object. Therefore, this creates a thread for the particular MultiThread instance.SDK Sample codefromdatetimeimportdatetimefromzcrmsdk.src.com.zoho.crm.api.user_signatureimportUserSignaturefromzcrmsdk.src.com.zoho.crm.api.dcimportUSDataCenterfromzcrmsdk.src.com.zoho.api.authenticator.storeimportDBStorefromzcrmsdk.src.com.zoho.api.loggerimportLoggerfromzcrmsdk.src.com.zoho.crm.api.initializerimportInitializerfromzcrmsdk.src.com.zoho.api.authenticator.oauth_tokenimportOAuthTokenfromzcrmsdk.src.com.zoho.crm.api.recordimport*fromzcrmsdk.src.com.zoho.crm.apiimportHeaderMap,ParameterMapfromzcrmsdk.src.com.zoho.crm.api.sdk_configimportSDKConfigclassRecord(object):def__init__(self):pass@staticmethoddefget_records():"""Create an instance of Logger Class that takes two parameters1 -> Level of the log messages to be logged. Can be configured by typing Logger.Levels "." and choose any level from the list displayed.2 -> Absolute file path, where messages need to be logged."""logger=Logger.get_instance(level=Logger.Levels.INFO,file_path="/Users/user_name/Documents/python_sdk_log.log")# Create an UserSignature instance that takes user Email as parameteruser=UserSignature(email="abc@zoho.com")"""Configure the environmentwhich is of the pattern Domain.EnvironmentAvailable Domains: USDataCenter, EUDataCenter, INDataCenter, CNDataCenter, AUDataCenterAvailable Environments: PRODUCTION(), DEVELOPER(), SANDBOX()"""environment=USDataCenter.PRODUCTION()"""Create a Token instance that takes the following parameters1 -> OAuth client id.2 -> OAuth client secret.3 -> Grant token.4 -> Refresh token.5 -> OAuth redirect URL.6 -> id"""token=OAuthToken(client_id="clientId",client_secret="clientSecret",grant_token="grant_token",refresh_token="refresh_token",redirect_url="redirectURL",id="id")"""Create an instance of TokenStore1 -> DataBase host name. Default value "localhost"2 -> DataBase name. Default value "zohooauth"3 -> DataBase user name. Default value "root"4 -> DataBase password. Default value ""5 -> DataBase port number. Default value "3306"6->  DataBase table name . Default value "oauthtoken""""store=DBStore()"""auto_refresh_fields (Default value is False)if True - all the modules' fields will be auto-refreshed in the background, every hour.if False - the fields will not be auto-refreshed in the background. The user can manually delete the file(s) or refresh the fields using methods from ModuleFieldsHandler(zcrmsdk/src/com/zoho/crm/api/util/module_fields_handler.py)pick_list_validation (Default value is True)A boolean field that validates user input for a pick list field and allows or disallows the addition of a new value to the list.if True - the SDK validates the input. If the value does not exist in the pick list, the SDK throws an error.if False - the SDK does not validate the input and makes the API request with the user’s input to the pick listconnect_timeout (Default value is None)A  Float field to set connect timeoutread_timeout (Default value is None)A  Float field to set read timeout"""config=SDKConfig(auto_refresh_fields=True,pick_list_validation=False,connect_timeout=None,read_timeout=None)"""The path containing the absolute directory path (in the key resource_path) to store user-specific files containing information about fields in modules."""resource_path='/Users/user_name/Documents/python-app'"""Call the static initialize method of Initializer class that takes the following arguments1 -> UserSignature instance2 -> Environment instance3 -> Token instance4 -> TokenStore instance5 -> SDKConfig instance6 -> resource_path7 -> Logger instance"""Initializer.initialize(user=user,environment=environment,token=token,store=store,sdk_config=config,resource_path=resource_path,logger=logger)try:module_api_name='Leads'param_instance=ParameterMap()param_instance.add(GetRecordsParam.converted,'both')param_instance.add(GetRecordsParam.cvid,'12712717217218')header_instance=HeaderMap()header_instance.add(GetRecordsHeader.if_modified_since,datetime.now())response=RecordOperations().get_records(module_api_name,param_instance,header_instance)ifresponseisnotNone:# Get the status code from responseprint('Status Code: '+str(response.get_status_code()))ifresponse.get_status_code()in[204,304]:print('No Content'ifresponse.get_status_code()==204else'Not Modified')return# Get object from responseresponse_object=response.get_object()ifresponse_objectisnotNone:# Check if expected ResponseWrapper instance is received.ifisinstance(response_object,ResponseWrapper):# Get the list of obtained Record instancesrecord_list=response_object.get_data()forrecordinrecord_list:# Get the ID of each Recordprint("Record ID: "+record.get_id())# Get the createdBy User instance of each Recordcreated_by=record.get_created_by()# Check if created_by is not Noneifcreated_byisnotNone:# Get the Name of the created_by Userprint("Record Created By - Name: "+created_by.get_name())# Get the ID of the created_by Userprint("Record Created By - ID: "+created_by.get_id())# Get the Email of the created_by Userprint("Record Created By - Email: "+created_by.get_email())# Get the CreatedTime of each Recordprint("Record CreatedTime: "+str(record.get_created_time()))ifrecord.get_modified_time()isnotNone:# Get the ModifiedTime of each Recordprint("Record ModifiedTime: "+str(record.get_modified_time()))# Get the modified_by User instance of each Recordmodified_by=record.get_modified_by()# Check if modified_by is not Noneifmodified_byisnotNone:# Get the Name of the modified_by Userprint("Record Modified By - Name: "+modified_by.get_name())# Get the ID of the modified_by Userprint("Record Modified By - ID: "+modified_by.get_id())# Get the Email of the modified_by Userprint("Record Modified By - Email: "+modified_by.get_email())# Get the list of obtained Tag instance of each Recordtags=record.get_tag()iftagsisnotNone:fortagintags:# Get the Name of each Tagprint("Record Tag Name: "+tag.get_name())# Get the Id of each Tagprint("Record Tag ID: "+tag.get_id())# To get particular field valueprint("Record Field Value: "+str(record.get_key_value('Last_Name')))print('Record KeyValues: ')forkey,valueinrecord.get_key_values().items():print(key+" : "+str(value))# Check if the request returned an exceptionelifisinstance(response_object,APIException):# Get the Statusprint("Status: "+response_object.get_status().get_value())# Get the Codeprint("Code: "+response_object.get_code().get_value())print("Details")# Get the details dictdetails=response_object.get_details()forkey,valueindetails.items():print(key+' : '+str(value))# Get the Messageprint("Message: "+response_object.get_message().get_value())exceptExceptionase:print(e)Record.get_records()