{ "info": { "author": "Devlin O'Brien", "author_email": "dobrien@my.ccsu.edu", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3" ], "description": "# RETSDK\n\nA Python SDK for the Real Estate Transaction Standard (RETS)\n\n## Installation\n\n```\npip install retsdk\n```\n\n## Usage\n\n### Initialize a client\n\nCreate a new RETSConnection instance to connect to a RETS server.\n\n##### Example\n```python\nfrom retsdk.client import RETSConnection\n\nrets = RETSConnection(\n username='your_rets_username',\n password='your_rets_password',\n login_url='https://rets.somemls.com/rets/Login/'\n)\n\n# Metadata info & transaction URLs get loaded automatically\nprint(rets.metadata_version)\n# 1.00.00235\n\nprint(rets.metadata_timestamp)\n# 2015-05-20T20:08:15Z\n\nprint(rets.min_metadata_timestamp)\n# 2015-05-20T20:08:15Z\n\nprint(rets.get_metadata_url)\n# https://rets.somemls.com/rets/GetMetadata/\n\nprint(rets.get_search_url)\n# https://rets.somemls.com/rets/Search/\n\nprint(rets.get_object_url)\n# https://rets.somemls.com/rets/GetObject/\n```\n\n##### Initialization Arguments\nArgument | Type | Required | Meaning\n------------ | ------------- | ------------- | -------------\nusername | String | Yes | RETS account username\npassword | String | Yes | RETS account password\nlogin_url | String | Yes | RETS server login URL\nauth_type | String | No | Authentication type (defaults to 'digest')\nrets_version | String | No | Specifies the RETS version to be used (defaults to 'RETS/1.7.2')\nuser_agent | String | No | Specifies the user-agent (defaults to 'RETSDK/1.0')\n\n\n### Download Metadata\n\nThere are (usually) several tiers of metadata to consider in a RETS system. These are resource metadata, class metadata, table metadata, and lookup-type metadata. RETSDK has methods to work with each of these programmatically, but if you would like to view metadata right in your browser with no additional setup, you can also try [RETSMD](https://retsmd.com/).\n\nAll of the metadata query methods return a response dictionary with the following items:\n\nKey | Meaning | Value \n------------ | ------------- | -------------\nmore_rows | Indicates whether there are more rows to download | Boolean\nok | Indicates whether the process completed successfully | Boolean\nrecord_count | The number of rows returned | Integer\nreply_code | The server's RETS reply code | Integer\nreply_text | The message accompanying the RETS reply code | String\nrows | The metadata records returned by the server | List\n\n\n#### 1. Resource Metadata\nResource metadata is the top layer of metadata; it tells you what resources are accessible from your account. Use the **get_resource_metadata()** method to download resource metadata.\n\n##### Arguments\nNone\n\n##### Example\n```python\n# Get the RETS system's resource metadata\nresponse = rets.get_resource_metadata()\n\nfrom pprint import pprint\npprint(response)\n#{'more_rows': False,\n# 'ok': True,\n# 'record_count': 2,\n# 'reply_code': '0',\n# 'reply_text': 'Operation Success.',\n# 'rows': [{'ClassCount': 1,\n# 'ClassDate': '2015-01-28T21:06:04Z',\n# 'ClassVersion': '1.00.00001',\n# 'Description': 'Agent',\n# 'KeyField': 'sysid',\n# 'LookupDate': '2015-01-21T17:31:54Z',\n# 'LookupVersion': '1.00.00001',\n# 'ObjectDate': '2014-03-21T17:15:24Z',\n# 'ObjectVersion': '1.00.00001',\n# 'ResourceID': 'Agent',\n# 'StandardName': 'Agent',\n# 'TableName': 'Agent',\n# 'VisibleName': 'Agent'},\n# {'ClassCount': 1,\n# 'ClassDate': '2015-01-28T16:19:02Z',\n# 'ClassVersion': '1.00.00001',\n# 'Description': 'Listing',\n# 'KeyField': 'sysid',\n# 'LookupDate': '2015-01-28T14:34:35Z',\n# 'LookupVersion': '1.00.00001',\n# 'ObjectDate': '2015-01-28T14:34:35Z',\n# 'ObjectVersion': '1.00.00001',\n# 'ResourceID': 'Property',\n# 'StandardName': 'Property',\n# 'TableName': 'Listing',\n# 'VisibleName': 'Listing'}]}\n\n```\n\n#### 2. Class Metadata\nClass metadata provides information about the classes in a resource. Use the **get_class_metadata()** method to get class metadata.\n\n##### Arguments\nArgument Name | Required | Meaning\n------------ | ------------- | -------------\nresource | No | The ID of a RETS resource. Defaults to 'Property'.\n\n##### Example\n```python\nclass_metadata_response = rets.get_class_metadata(resource='Property')\n\npprint(class_metadata_response)\n#{'more_rows': False,\n# 'ok': True,\n# 'record_count': 1,\n# 'reply_code': '0',\n# 'reply_text': 'Operation Success.',\n# 'rows': [{'ClassName': 'Listing',\n# 'Description': 'Cross Property',\n# 'StandardName': None,\n# 'TableDate': '2015-01-28T02:49:39Z',\n# 'TableVersion': '1.00.00985',\n# 'UpdateDate': '2015-01-28T14:32:06Z',\n# 'UpdateVersion': '1.00.00001',\n# 'VisibleName': 'Cross Property'}]}\n```\n\n#### 3. Table Metadata\nTable metadata tells you about the specific fields available in a class. Use the **get_table_metadata()** method to get table metadata.\n\n##### Arguments\nArgument Name | Required | Meaning\n------------ | ------------- | -------------\nresource | No | The ID of a resource. Defaults to 'Property'.\nclass_name | No | The class name or system name of a class within a resource. Defaults to 'Listing'.\n\n##### Example\n```python\ntable_metadata_response = rets.get_table_metadata(\n resource='Property', \n class_name='Listing'\n)\n\npprint(table_metadata_response)\n#{'more_rows': False,\n# 'ok': True,\n# 'record_count': 2,\n# 'reply_code': '0',\n# 'reply_text': 'Operation Success.',\n# 'rows': [{'Alignment': 'Left',\n# 'DBName': 'price',\n# 'DataType': 'Int',\n# 'Default': None,\n# 'InKeyIndex': 0,\n# 'Index': 1,\n# 'Interpretation': 'Number',\n# 'LongName': 'Price',\n# 'LookupName': None,\n# 'MaxSelect': None,\n# 'Maximum': 2147483647,\n# 'MaximumLength': 11,\n# 'MetadataEntryID': 200,\n# 'Minimum': -2147483648,\n# 'Precision': None,\n# 'Required': 1,\n# 'Searchable': 1,\n# 'ShortName': 'Price',\n# 'StandardName': 'Price',\n# 'SystemName': 'Price',\n# 'Unique': 0,\n# 'Units': 'USD',\n# 'UseSeparator': None},\n# {'Alignment': 'Left',\n# 'DBName': 'property_type',\n# 'DataType': 'Character',\n# 'Default': None,\n# 'InKeyIndex': 0,\n# 'Index': 1,\n# 'Interpretation': 'Lookup',\n# 'LongName': 'Property Type',\n# 'LookupName': 'PropertyType',\n# 'MaxSelect': None,\n# 'Maximum': None,\n# 'MaximumLength': 32,\n# 'MetadataEntryID': 201,\n# 'Minimum': None,\n# 'Precision': None,\n# 'Required': 1,\n# 'Searchable': 1,\n# 'ShortName': 'PropertyType',\n# 'StandardName': 'PropertyType',\n# 'SystemName': 'PropertyType',\n# 'Unique': 0,\n# 'Units': None,\n# 'UseSeparator': None}]}\n```\n\n#### 4. Lookup-Type Metadata\nThe last type of metadata data to consider is lookup-type metdata. If a field in the table metadata has an interpretation of \"Lookup\", there is a list of specific values that the field can hold. Get this list of values with the **get_lookup_type_metadata()** method.\n\n##### Arguments\nArgument Name | Required | Meaning\n------------ | ------------- | -------------\nresource | No | The ID of a resource. Defaults to 'Property'.\nlookup_name | Yes | A field's lookup name.\n\n##### Example\n```python\nlookup_type_metadata_response = rets.get_lookup_type_metadata(\n resource='Property',\n lookup_name='PropertyType'\n)\n\npprint(lookup_type_metadata_response)\n# {'more_rows': False,\n# 'ok': True,\n# 'record_count': 3,\n# 'reply_code': '0',\n# 'reply_text': 'Operation Success.',\n# 'rows': [{'LongValue': 'Single Family Detached',\n# 'MetadataEntryID': 10002,\n# 'ShortValue': 'SFD',\n# 'Value': 'SFD'},\n# {'LongValue': 'Condominium',\n# 'MetadataEntryID': 10003,\n# 'ShortValue': 'CON',\n# 'Value': 'CON'},\n# {'LongValue': 'Multifamily',\n# 'MetadataEntryID': 10004,\n# 'ShortValue': 'MUL',\n# 'Value': 'MUL'}]}\n```\n\n\n### Download Data\n\nDownload data or search through records using the **get_data()** method. You will need to specify a query (using DMQL) and a list of the fields that you would like to have returned to you (see *Download Metadata* to learn how to find out what fields are available).\n\n##### Arguments\nArgument Name | Required | Meaning\n------------ | ------------- | -------------\nresource | Yes | The ID of a RETS system resource.\nclass_name | Yes | The name of a class in the specified resource.\nquery | Yes | A DMQL query\nfields | Yes | A list of the fields to be returned\ndata_format | No | The RETS data format to be used with fields. Defaults to 'COMPACT-DECODED'.\nlimit | No | The maximum number of records to return\noffset | No | An offset position that can be used with limit\n\n##### Response Dictionary\nKey | Meaning | Value Type\n------------ | ------------- | -------------\nmore_rows | Indicates whether there are more rows to download for the current query | Boolean\nok | Indicates whether the query was processed successfully | Boolean\nrecord_count | The number of rows returned | Integer\nreply_code | The server's RETS reply code | Integer\nreply_text | The message accompanying the RETS reply code | String\nrows | The actual records returned by the query | List\n\n##### Examples\n```python\n# Query all listings with \"SFD\" PropertyType, return only MLSNumber and Price\nrets_query = \"(PropertyType=SFD)\"\nfields_to_be_downloaded = [\"MLSNumber\", \"Price\"]\n\ndata = rets.get_data(\n resource='Property',\n class_name='Property',\n query=query,\n fields=fields_to_be_downloaded,\n)\n\npprint(data)\n# {'more_rows': False,\n# 'ok': True,\n# 'record_count': '10',\n# 'reply_code': '0',\n# 'reply_text': 'Operation Success.',\n# 'rows': [{'Price': 199000.0, 'MLSNumber': 'MLS0000001'},\n# {'Price': 2500000.0, 'MLSNumber': 'MLS0000002'},\n# {'Price': 319500.0, 'MLSNumber': 'MLS0000003'},\n# {'Price': 275900.0, 'MLSNumber': 'MLS0000004'},\n# {'Price': 239900.0, 'MLSNumber': 'MLS0000005'},\n# {'Price': 339900.0, 'MLSNumber': 'MLS0000006'},\n# {'Price': 249900.0, 'MLSNumber': 'MLS0000007'},\n# {'Price': 219900.0, 'MLSNumber': 'MLS0000008'},\n# {'Price': 579900.0, 'MLSNumber': 'MLS0000009'},\n# {'Price': 209900.0, 'MLSNumber': 'MLS0000010'}]}\n\n```\n\nYou may optionally use the **limit** and **offset** parameters to page the data that you want to download. This allows you to break large downloads into smaller pieces.\n\n```python\n# A broader RETS query that might returns lots of records\nrets_query=(PropertyType=SFD,CON,MUL)\nfields_to_be_downloaded = [\"MLSNumber\", \"Price\"]\n\n# Use a loop to download 10 records at a time\ndownload_complete = False\nlast_offset = 0\nwhile not download_complete:\n data = rets.get_data(\n resource='Property',\n class_name='Listing',\n query=rets_query,\n fields=fields_to_be_downloaded,\n limit=10,\n offset=last_offset\n )\n\n for row in data['rows']:\n # Do something with the rows you downloaded here (save to a database, etc.)\n pass\n\n if data['more_rows']:\n # Still more to download!\n last_offset += 10\n else:\n # Done!\n download_complete = True\n\n```\n\n#### Getting a Record Count without Returning Data\nIf you just want a count of how many records match your query, you can use **get_count()** instead of get_data(). get_count() will return an integer instead of a full response dictionary.\n\nYou do not specify fields, limit, or offset with get_count(), but otherwise it works just like get_data(). It is, in fact, just another wrapper for the RETS Search transaction with the *Count* parameter set differently.\n\n##### Example\n```python\nrow_count = rets.get_count(\n 'Property',\n class_name='Property',\n query=rets_query\n)\n\nprint(row_count)\n# 105\n```\n\n\n### Download Images\nUse the **get_object()** method to download images. This method is a wrapper for the RETS specification's GetObject transaction.\n\nget_object() returns a response dictionary, but the dictionary does not contain 'rows', 'record_count' or 'more_rows'. Instead, it will contain an item called 'object_data', where the actual object data will be stored as bytes.\n\n##### Arguments\nArgument Name | Required | Meaning\n------------ | ------------- | -------------\nresource | Yes | The ID of a RETS system resource.\nobj_type | Yes | The type of object to be returned (e.g., 'Photo').\nobj_id | Yes | The system ID of the record associated with object.\norder_no | No | The order number of the image or other object (for situations where there are multiple images associated with one listing)\npath | No | A file system path where image data should be written (used only when write=True).\nwrite | No | A boolean value that can optionally be set to True if you would like get_object() to write image/object data to a file for you. You must specifiy a path if you wish to use this option.\n\n##### Response Dictionary:\n\nKey | Meaning | Value Type\n------------ | ------------- | ------------- \nok | Indicates whether the object download was successful | Boolean\nreply_code | The server's RETS reply code | Integer\nreply_text | The message accompanying the RETS reply code | String\nobject_data | The object data payload | Bytes\n\n##### Example\n```python\n# Download an image (as bytes)\nimg_response = rets.get_object(\n resource='Property',\n obj_type='Photo',\n obj_id='MLS0000001',\n order_no=0\n)\n\n# Write the image data to a file somewhere\npath = \"/tmp/rets/images/MLS0000001_01.jpg\"\nif img_response['ok']:\n with open(path, 'wb') as f:\n f.write(img_response['object_data'])\n\n```\n\n\n### Logout\nIf you would like to, you can close your RETS session with the **logout()** method.\n\n##### Arguments\nNone\n\n##### Response Dictionary:\nKey | Meaning | Value Type\n------------ | ------------- | -------------\nmore_rows | Indicates whether there are more rows to download for the current transaction | Boolean\nok | Indicates whether the transaction was successful | Boolean\nrecord_count | The number of rows returned | Integer\nreply_code | The server's RETS reply code | Integer\nreply_text | The message accompanying the RETS reply code | String\nrows | The actual records returned by the logout transaction | List\n\n##### Example\n```python\nlogout_response = rets.logout()\n\npprint(logout_response)\n# {'more_rows': False,\n# 'ok': True,\n# 'record_count': 1,\n# 'reply_code': '0',\n# 'reply_text': 'Operation Success.',\n# 'rows': [{'SignOffMessage': 'Connection Closed'}]}\n```\n\n### Exceptions\nRETSDK raises these exceptions when stuff goes wrong:\n\nException | Meaning\n------------ | -------------\nretsdk.exceptions.AuthenticationError | Raised when an unsupported authentication type is specified during intialization\nretsdk.exceptions.RequestError | Raised if a RETS transaction request cannot be completed\nretsdk.exceptions.TransactionError | Raised if the user attempts to perform a transaction that is not supported by the current RETS account.\n\n\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/5150brien/retsdk", "keywords": "rets real estate", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "retsdk", "package_url": "https://pypi.org/project/retsdk/", "platform": "", "project_url": "https://pypi.org/project/retsdk/", "project_urls": { "Homepage": "https://github.com/5150brien/retsdk" }, "release_url": "https://pypi.org/project/retsdk/1.0.1/", "requires_dist": null, "requires_python": ">=3", "summary": "Python SDK for the real estate transaction standard (RETS)", "version": "1.0.1" }, "last_serial": 5259878, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "3526e2bcd5f14274b2407b8cc19ad009", "sha256": "b215ef5b0f0cd31e25587d3a2a1b1bb296e98e8854a2603b3783f7cd11f9f1e0" }, "downloads": -1, "filename": "retsdk-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "3526e2bcd5f14274b2407b8cc19ad009", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3", "size": 15377, "upload_time": "2019-04-25T16:21:52", "url": "https://files.pythonhosted.org/packages/52/78/edbb13c1a917993b9d3946665bfc056d7f1ab797964d5995b856df1d3734/retsdk-1.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6da9026195ef3798826b2ccb8e71ce80", "sha256": "c068e0e5cb572a38a0b67d1c850e761677fce98a628260e31ea36cd1dce6eaae" }, "downloads": -1, "filename": "retsdk-1.0.0.tar.gz", "has_sig": false, "md5_digest": "6da9026195ef3798826b2ccb8e71ce80", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3", "size": 15013, "upload_time": "2019-04-25T16:22:02", "url": "https://files.pythonhosted.org/packages/10/f5/9600f31a733a2dc2f971b17014ec1cfdc12e0484c2b9d09a4520a2759154/retsdk-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "003401cd3319c7ae02f7d15a688e0a1f", "sha256": "e5e477f4659c714fb03438c64a72ae631bce6ecc6122b2ddfd38fe2715a10289" }, "downloads": -1, "filename": "retsdk-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "003401cd3319c7ae02f7d15a688e0a1f", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3", "size": 15464, "upload_time": "2019-05-12T20:58:08", "url": "https://files.pythonhosted.org/packages/d6/92/0e0f19b2059ee7765b75e883d93e728bf004a5070aef1a70045e6a76d2d6/retsdk-1.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ea483aa1e61b0a2ca28610b5b5906d4f", "sha256": "da2bcdb781039f488a24772c4629ce9c6041e81991e9e8f410db18b59876fec9" }, "downloads": -1, "filename": "retsdk-1.0.1.tar.gz", "has_sig": false, "md5_digest": "ea483aa1e61b0a2ca28610b5b5906d4f", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3", "size": 15112, "upload_time": "2019-05-12T20:58:10", "url": "https://files.pythonhosted.org/packages/58/ae/aa477187745fc1e4d0f34822fdc37a890ec678406a3f3987fce6a345a852/retsdk-1.0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "003401cd3319c7ae02f7d15a688e0a1f", "sha256": "e5e477f4659c714fb03438c64a72ae631bce6ecc6122b2ddfd38fe2715a10289" }, "downloads": -1, "filename": "retsdk-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "003401cd3319c7ae02f7d15a688e0a1f", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3", "size": 15464, "upload_time": "2019-05-12T20:58:08", "url": "https://files.pythonhosted.org/packages/d6/92/0e0f19b2059ee7765b75e883d93e728bf004a5070aef1a70045e6a76d2d6/retsdk-1.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ea483aa1e61b0a2ca28610b5b5906d4f", "sha256": "da2bcdb781039f488a24772c4629ce9c6041e81991e9e8f410db18b59876fec9" }, "downloads": -1, "filename": "retsdk-1.0.1.tar.gz", "has_sig": false, "md5_digest": "ea483aa1e61b0a2ca28610b5b5906d4f", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3", "size": 15112, "upload_time": "2019-05-12T20:58:10", "url": "https://files.pythonhosted.org/packages/58/ae/aa477187745fc1e4d0f34822fdc37a890ec678406a3f3987fce6a345a852/retsdk-1.0.1.tar.gz" } ] }