|
|
@@ -16,8 +16,6 @@ import logging
|
|
|
|
|
|
### TO DO ###
|
|
|
#
|
|
|
-# email results
|
|
|
-# allow this script to be called and work by itself (if __name__ == __main__)
|
|
|
# Print useful reports (land only, house and land, etc)
|
|
|
# Check if db entries no longer appear online (mark expired)
|
|
|
# When checking online from various sites, check if address already exists in db
|
|
|
@@ -57,37 +55,49 @@ class Property:
|
|
|
self.description = description
|
|
|
self.link = link
|
|
|
|
|
|
-
|
|
|
+class Parameters:
|
|
|
+ '''Parameters taken from config file'''
|
|
|
+ def __init__(self, file='landsearch.conf'):
|
|
|
+ self.file = file
|
|
|
+ if not path.exists(self.file):
|
|
|
+ raise FileNotFoundError("The config file cannot be opened", self.file)
|
|
|
+ try:
|
|
|
+ self.config = ConfigParser()
|
|
|
+ self.config.read(self.file)
|
|
|
+ self.search_params = self.config['Search']
|
|
|
+ self.log_params = self.config['Logging']
|
|
|
+ except Exception as err:
|
|
|
+ print(err, "Using default search Parameters")
|
|
|
|
|
|
class Search:
|
|
|
- """Universal Search Criteria"""
|
|
|
+ '''Universal Search Criteria'''
|
|
|
|
|
|
def checktype(self, attribute):
|
|
|
+ '''Fixes string None in config file and converts to '' '''
|
|
|
if not attribute == 'None':
|
|
|
return attribute
|
|
|
else:
|
|
|
return ''
|
|
|
|
|
|
-
|
|
|
-# def __init__(self, county: list, lower_price=0, upper_price=500000, \
|
|
|
-# lower_acres=5, upper_acres=15, type=['farm','land','home'], lower_sqft='', upper_sqft='', \
|
|
|
-# lower_bedrooms='', upper_bedrooms=''):
|
|
|
def __init__(self, file = 'landsearch.conf'):
|
|
|
- self.file = file
|
|
|
- if not path.exists(self.file):
|
|
|
- raise FileNotFoundError("The config file cannot be opened", self.file)
|
|
|
- try:
|
|
|
- config = ConfigParser()
|
|
|
- config.read(self.file)
|
|
|
- search_params = config['Search']
|
|
|
- log_params = config['Logging']
|
|
|
- except FileNotFoundError as err:
|
|
|
- print(err, "Using default search parameters.")
|
|
|
- except Exception as err:
|
|
|
- print(err, "Using default search parameters.")
|
|
|
+ # self.file = file
|
|
|
+ # if not path.exists(self.file):
|
|
|
+ # raise FileNotFoundError("The config file cannot be opened", self.file)
|
|
|
+ # try:
|
|
|
+ # config = ConfigParser()
|
|
|
+ # config.read(self.file)
|
|
|
+ # search_params = config['Search']
|
|
|
+ # log_params = config['Logging']
|
|
|
+ # except FileNotFoundError as err:
|
|
|
+ # print(err, "Using default search parameters.")
|
|
|
+ # except Exception as err:
|
|
|
+ # print(err, "Using default search parameters.")
|
|
|
+ params = Parameters()
|
|
|
+ search_params = params.search_params
|
|
|
+ log_params = params.log_params
|
|
|
|
|
|
logging.basicConfig(filename=log_params.get('log_file'), \
|
|
|
- level=log_params.get('logging_level', 30), \
|
|
|
+ level=int(log_params.get('logging_level', 30)), \
|
|
|
format='%(asctime)s %(levelname)-8s %(message)s', \
|
|
|
datefmt='%Y-%m-%d %H:%M:%S') ## Default log level WARNING (30)
|
|
|
logging.getLogger("urllib3").setLevel(logging.WARNING) ## Supress Requests method logging
|
|
|
@@ -112,14 +122,6 @@ class Search:
|
|
|
self.upper_sqft = self.checktype(search_params.get('upper_sqft', ''))
|
|
|
self.lower_bedrooms = self.checktype(search_params.get('lower_bedrooms', ''))
|
|
|
self.upper_bedrooms = self.checktype(search_params.get('upper_bedrooms', ''))
|
|
|
-# self.lower_price = search_params.get('lower_price', 0)
|
|
|
-# self.upper_price = search_params.get('upper_price', 525000)
|
|
|
-# self.lower_acres = search_params.get('lower_acres', 5)
|
|
|
-# self.upper_acres = search_params.get('upper_acres', 15)
|
|
|
-# self.lower_sqft = search_params.get('lower_sqft', '')
|
|
|
-# self.upper_sqft = search_params.get('upper_sqft', '')
|
|
|
-# self.lower_bedrooms = search_params.get('lower_bedrooms', '')
|
|
|
-# self.upper_bedrooms = search_params.get('upper_bedrooms', '')
|
|
|
|
|
|
for property_type in self.type:
|
|
|
assert property_type in self.types, ("Unknown type '" + property_type + "'. Property Type must be of type: " + str(self.types))
|
|
|
@@ -127,7 +129,6 @@ class Search:
|
|
|
## FOR TESTING, PRINT ALL ATTRIBUTES OF SEARCH ##
|
|
|
logging.debug(vars(self))
|
|
|
|
|
|
-
|
|
|
class ImproperSearchError(Exception):
|
|
|
def __init__ (self, search, message="Improper Search. Must use instance of Search class"):
|
|
|
self.search = search
|
|
|
@@ -147,6 +148,8 @@ class MLSDATA:
|
|
|
self.cursor = ''
|
|
|
self.cnx = ''
|
|
|
self.new_listings = []
|
|
|
+ self.email = Parameters().search_params['email']
|
|
|
+ print("EMAIL: " + self.email)
|
|
|
|
|
|
def stringbuilder(self, search: Search, county):
|
|
|
""" Takes Search class and build appropriate URL query based on mlstype. Currently only supports gmls."""
|
|
|
@@ -271,14 +274,9 @@ class MLSDATA:
|
|
|
See class search for more information.
|
|
|
--> 9/1/20 - takes Search class as argument. All properties are handled by the class <--"""
|
|
|
if isinstance(search, Search):
|
|
|
-
|
|
|
-##
|
|
|
-# PROGRAM BREAKS HERE - Used to loop for each county, not Search class contains list of counties. Need to automate looping.
|
|
|
-##
|
|
|
-
|
|
|
- if not county in self.counties: ### FIX for lower()
|
|
|
+ if not county in self.counties: ### FIX for lower()
|
|
|
print("County " + county + " not regognized. Exiting")
|
|
|
- else:
|
|
|
+ else:
|
|
|
print("Scanning for results in " + county + " using the " + self.mlstype.upper() + " database.")
|
|
|
if self.mlstype == 'gmls':
|
|
|
list = self.gmlsparser(self.stringbuilder(search, county), county)
|
|
|
@@ -318,7 +316,6 @@ class MLSDATA:
|
|
|
Returns distance in METERS (1m = 0.000621371 mi) and time in SECONDS
|
|
|
returns fully populated Propery object."""
|
|
|
print("Fetching live Google Data. $$")
|
|
|
-# Build Request
|
|
|
destination1 = 'Hebron Christian Acadamy' ## Working query for Hebron Christian Acadamy
|
|
|
destination2 = 'JHRJ+FJ Atlanta, Georgia' ## Plus code for Hourly parking at Int'l Terminal, KATL
|
|
|
params = {}
|
|
|
@@ -341,14 +338,6 @@ class MLSDATA:
|
|
|
except:
|
|
|
print("ERROR: Failed to obtain Google API data")
|
|
|
|
|
|
-#Load sample data for testing:
|
|
|
-# with open('complex.json') as f:
|
|
|
-# data = json.load(f)
|
|
|
-# google_result = data
|
|
|
-### end testing json ###
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
def insertrecord(self, property, work_address=None, school_address=None):
|
|
|
"""Inserts record into database. Takes argument Property class object.
|
|
|
FUTURE - add date_added field to insert operation."""
|
|
|
@@ -396,42 +385,45 @@ class MLSDATA:
|
|
|
def alerts(self):
|
|
|
pass
|
|
|
|
|
|
- def email(self):
|
|
|
- body = ''
|
|
|
- data = []
|
|
|
- subj = "New Real Estate Listings for " + str(datetime.date.today())
|
|
|
- for listing in self.new_listings:
|
|
|
- row = []
|
|
|
- body += listing.MLS + " | " + listing.address + " | " + listing.acres + " | " + listing.price + " | " + listing.link + "\n"
|
|
|
- row.append(listing.MLS)
|
|
|
- row.append(listing.address)
|
|
|
- row.append('{:0,.2f}'.format(float(listing.acres)))
|
|
|
- row.append(listing.sqft)
|
|
|
- row.append('${:0,.0f}'.format(int(listing.price)))
|
|
|
- row.append(listing.time_to_school/60 if hasattr(listing, 'time_to_school') else 'NA')
|
|
|
- row.append(listing.link)
|
|
|
- data.append(row)
|
|
|
- body = """\
|
|
|
-Daily Real Estate Search Report\n
|
|
|
-The following properties have been found which may be of interest.\n
|
|
|
-"""
|
|
|
- results = tabulate(data, headers=['MLS', 'Address', 'Acres', 'sqft', 'Price', 'Time to School', 'link'])
|
|
|
- body += results
|
|
|
- sendto = ['stagl.mike@gmail.com', 'M_Stagl@hotmail.com']
|
|
|
- mymail = custom_email.simplemail(subj, body, sendto)
|
|
|
-
|
|
|
- if len(self.new_listings) > 0:
|
|
|
- try:
|
|
|
- mymail.sendmail()
|
|
|
- except Exception as e:
|
|
|
- print("Error sending email. " + e)
|
|
|
+ def email_results(self):
|
|
|
+ pass
|
|
|
+ if self.email:
|
|
|
+ logging.debug("email_results" + str(self.email))
|
|
|
+ body = ''
|
|
|
+ data = []
|
|
|
+ subj = "New Real Estate Listings for " + str(datetime.date.today())
|
|
|
+ for listing in self.new_listings:
|
|
|
+ row = []
|
|
|
+ body += listing.MLS + " | " + listing.address + " | " + listing.acres + " | " + listing.price + " | " + listing.link + "\n"
|
|
|
+ row.append(listing.MLS)
|
|
|
+ row.append(listing.address)
|
|
|
+ row.append('{:0,.2f}'.format(float(listing.acres)))
|
|
|
+ row.append(listing.sqft)
|
|
|
+ row.append('${:0,.0f}'.format(int(listing.price)))
|
|
|
+ row.append(listing.time_to_school/60 if hasattr(listing, 'time_to_school') else 'NA')
|
|
|
+ row.append(listing.link)
|
|
|
+ data.append(row)
|
|
|
+ body = """\
|
|
|
+ Daily Real Estate Search Report\n
|
|
|
+ The following properties have been found which may be of interest.\n
|
|
|
+ """
|
|
|
+ results = tabulate(data, headers=['MLS', 'Address', 'Acres', 'sqft', 'Price', 'Time to School', 'link'])
|
|
|
+ body += results
|
|
|
+ sendto = ['stagl.mike@gmail.com', 'M_Stagl@hotmail.com']
|
|
|
+ mymail = custom_email.simplemail(subj, body, sendto)
|
|
|
+
|
|
|
+ if len(self.new_listings) > 0:
|
|
|
+ try:
|
|
|
+ mymail.sendmail()
|
|
|
+ except Exception as e:
|
|
|
+ print("Error sending email. " + e)
|
|
|
+ logging.warning("Error sending email. " + e)
|
|
|
+ else:
|
|
|
+ print("No new listings. Email not sent")
|
|
|
+ logging.info("No new listings. Email not sent")
|
|
|
else:
|
|
|
- print("No new listings. Email not sent")
|
|
|
-# REMOVE AFTER TESTING #
|
|
|
- mymail.sendmail()
|
|
|
-########################
|
|
|
-
|
|
|
-########### BEGIN CODE ###############33
|
|
|
+ print("Suppressing email based on landsearch.conf preferences.")
|
|
|
+ logging.warning("Suppressing email based on landsearch.conf preferences.")
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
@@ -439,8 +431,6 @@ if __name__ == '__main__':
|
|
|
gmls = MLSDATA('GMLS') # Create MLSDATA object
|
|
|
|
|
|
mysearch = Search() # Create a custom search object
|
|
|
-# print(len(mysearch.county))
|
|
|
-# print(mysearch.county[0])
|
|
|
myresults = []
|
|
|
|
|
|
## Create function in MLSDATA module:
|
|
|
@@ -464,33 +454,7 @@ if __name__ == '__main__':
|
|
|
gmls.connectdb()
|
|
|
gmls.dbinsert(myresults)
|
|
|
gmls.closedb()
|
|
|
-#
|
|
|
-# gmls.email()
|
|
|
-#
|
|
|
-#print()
|
|
|
-#print(str(len(gmls.new_listings)) + " new properties found!")
|
|
|
-#print()
|
|
|
-#for listing in gmls.new_listings:
|
|
|
-# print(listing.MLS, listing.address)
|
|
|
-# gmls = MLSDATA('GMLS')
|
|
|
-#
|
|
|
-# #new_properties = []
|
|
|
-#
|
|
|
-## for county in ['Jackson']: ### FIX
|
|
|
-# for county in gmls.counties: ### FIX
|
|
|
-# mysearch = Search(county, type=['farm', 'house', 'land'], upper_price=525000) ### FIX
|
|
|
-# mydata = gmls.getmlsdata(mysearch)
|
|
|
-#
|
|
|
-# gmls.connectdb()
|
|
|
-# gmls.dbinsert(mydata)
|
|
|
-# gmls.closedb()
|
|
|
-#
|
|
|
-# gmls.email()
|
|
|
-#
|
|
|
-#print()
|
|
|
-#print(str(len(gmls.new_listings)) + " new properties found!")
|
|
|
-#print()
|
|
|
-#for listing in gmls.new_listings:
|
|
|
-# print(listing.MLS, listing.address)
|
|
|
-#
|
|
|
-#
|
|
|
+
|
|
|
+ gmls.email_results()
|
|
|
+
|
|
|
+
|