-
-
Save dat-vikash/2280dd53f37a6406fb73b719dc6f8cb2 to your computer and use it in GitHub Desktop.
| """ | |
| Helps make dad life easier by reserving times before pre-k. | |
| Features: | |
| - allow for filtering by time, day of week, number of player and course | |
| - handles reservations and ensures no overbooking | |
| - keeps track of default and state | |
| Requires python3 and selenium: | |
| + https://sites.google.com/chromium.org/driver/ | |
| + https://selenium-python.readthedocs.io/ | |
| """ | |
| try: | |
| from urllib.request import Request, urlopen # Python 3 | |
| except ImportError: | |
| from urllib2 import Request, urlopen # Python 2 | |
| import json | |
| import datetime | |
| import time | |
| import os | |
| from selenium import webdriver | |
| import pytz | |
| COURSEIDS_MAP = { | |
| "B": {"name":"Francis A. Byrne Golf Course", "id":"54f14d8a0c8ad60378b03e95"}, | |
| "H": {"name":"Hendricks Field Golf Course", "id":"54f14d8a0c8ad60378b03e98"}, | |
| "W": {"name":"Weequahic Park Golf Course", "id":"54f14d8b0c8ad60378b03e9c"} | |
| } | |
| local_tz = pytz.timezone('US/Eastern') | |
| AUTO_RESERVATION=False | |
| def login(driver): | |
| """ | |
| login to site | |
| """ | |
| driver.get(f"https://essex-group.book.teeitup.golf/login") | |
| time.sleep(5) | |
| username = driver.find_element("id","txtUsername") | |
| password = driver.find_element("id","txtPassword") | |
| # FILL in your county log in here | |
| username.send_keys("") | |
| password.send_keys("") | |
| # Login and return driver with auth context | |
| driver.find_element("xpath","//button[@data-testid='login-button']").click() | |
| return driver | |
| def reserve(tees): | |
| """ | |
| does the actual reservation | |
| """ | |
| reservations = load_reservations() | |
| reservations_weekly_max = tees[0]['rounds_per_week'] | |
| reservations_daily_max = tees[0]['rounds_per_day'] | |
| # todo: get existing reservations | |
| # existing_reservations = 0 | |
| # | |
| # reservations_weekly_max -= existing_reservations | |
| # reservations_daily_max -= existing_reservations | |
| drivers = [] | |
| for tee in tees: | |
| local_time = tee['teetime'].astimezone(local_tz) | |
| course_name = list( | |
| filter(None, | |
| [COURSEIDS_MAP[c]['name'] if COURSEIDS_MAP[c]['id'] == tee['course'] else '' for c in COURSEIDS_MAP] | |
| ) | |
| )[0] | |
| if int(reservations['daily'].get(tee['date'], reservations_daily_max)) > 0 \ | |
| and int(reservations['weekly'].get(local_time.strftime("%V"), reservations_weekly_max)) > 0 : # weekly check | |
| print(f"Reserving Tee time for Date: {tee['date'] } Time: {local_time.strftime('%H:%M %p')} Course: {course_name} Available: {tee['playersAvail']} Fee: ${tee['fee']/100} Booking: {tee['num_player_booking']}") | |
| # start driver and get auth context | |
| driver = webdriver.Chrome() | |
| driver = login(driver) | |
| time.sleep(5) | |
| drivers.append(driver) | |
| # time to book it | |
| driver.get(f"https://essex-group.book.teeitup.golf/?course={tee['course']}&date={tee['date']}") | |
| t = tee['teetime'].strftime('%-H:%M %p') | |
| time.sleep(5) | |
| # find the tee time | |
| button = driver.find_element(f"xpath",f"//p[@data-testid='teetimes-tile-time'][contains(., '{t}')]").find_element("xpath","./../..").find_element("xpath","//button[@data-testid='teetimes_book_now_button']") | |
| button.click() | |
| time.sleep(3) | |
| # checkout | |
| driver.find_element("xpath",f"//button[@data-testid='button-value-{tee['num_player_booking']}']").click() | |
| driver.find_element("xpath","//span[contains(.,'Proceed to Checkout')]").click() | |
| time.sleep(5) | |
| # no turning back | |
| driver.find_element("name","chb-nm").click() | |
| # todo: actually checkout | |
| # driver.find_element("xpath","//button[@data-testid='make-your-reservation-btn']").click() | |
| # update reservations | |
| reservations['daily'].update({tee['date']: reservations['daily'].get(tee['date'],reservations_daily_max) - 1 }) | |
| reservations['weekly'].update({local_time.strftime("%V"): reservations['weekly'].get(local_time.strftime("%V"),reservations_weekly_max) - 1 }) | |
| else: | |
| print(f"Daily/Weekly Max Filled for Reserving Tee time for Date: {tee['date'] } Time: {local_time.strftime('%H:%M %p')} Course: {course_name} Available: {tee['playersAvail']} Fee: ${tee['fee']/100} Booking: {tee['num_player_booking']}") | |
| time.sleep(5) | |
| for d in drivers: | |
| d.quit() | |
| write_reservations(reservations) | |
| def banner(): | |
| banner = """ | |
| '\ . . |>18>> | |
| \ . ' . | | |
| O>> . 'o | | |
| \ . | | |
| /\ . | | |
| / / .' | | |
| jgs^^^^^^^`^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| """ | |
| print(banner) | |
| def ranger(teetimes): | |
| for t in teetimes: | |
| print("Date: " + t['date'] + " Time: " + t['teetime'].strftime('%H:%M')) | |
| count = 0 | |
| while count < 300: | |
| if count % 30 : | |
| os.system('say -v "Victoria" "TEE TIMES FOUND, HURRY UP"') | |
| time.sleep(1) | |
| count += 1 | |
| def load_reservations(): | |
| data = [] | |
| if os.path.exists('reservations.json'): | |
| f = open('reservations.json') | |
| data = json.load(f) | |
| f.close() | |
| else: | |
| data = {'weekly':{}, 'daily':{}} | |
| return data | |
| def write_reservations(payload): | |
| if os.path.exists('reservations.json'): | |
| pass | |
| else: | |
| print("reservations File created.") | |
| with open('reservations.json', "w") as outfile: | |
| outfile.write(json.dumps(payload)) | |
| return | |
| def load_defaults(): | |
| data = None | |
| # Opening JSON file | |
| if os.path.exists('config.json'): | |
| f = open('config.json') | |
| data = json.load(f) | |
| f.close() | |
| return data | |
| def write_defaults(payload): | |
| if os.path.exists('config.json'): | |
| pass | |
| else: | |
| print("Default File created.") | |
| with open('config.json', "w") as outfile: | |
| outfile.write(json.dumps(payload)) | |
| return | |
| def get_filters(): | |
| # load defaults | |
| defaults = load_defaults() | |
| if defaults is None: | |
| print("""" | |
| ##### Inital Setup: Fill out scorecard #### | |
| """) | |
| # get round details | |
| starting_tee_time = int(input("Enter the earliest hour you can tee off in 24 hour time (ex: 7 for 7 am, 13 for 1 pm): ")) | |
| ending_tee_time = int(input("Enter the latest hour you can tee off in 24 hour time (ex: 7 for 7 am, 13 for 1 pm): ")) | |
| num_players = int(input("How many players to reserve?: ")) | |
| auto_reserve = input("Should I auto reserve for you (y for yes, n for no)?: ") | |
| # print out course | |
| print("\n### Course's Available ###") | |
| [print(key + " - " + COURSEIDS_MAP[key]['name']) for key in COURSEIDS_MAP.keys()] | |
| print("A - ALL") | |
| courses = input(f"Which course(s) to search?: ") or "A" | |
| dow = input("""Which day(s) of the week: | |
| 1 = Monday | |
| 2 = Tuesday | |
| 3 = Wednesday | |
| 4 = Thursday | |
| 5 = Friday | |
| 6 = Saturday | |
| 7 = Sunday | |
| A = ALL | |
| Ex: 1,2 for mon/tues or 1 for mon only or A for all): """) or "A" | |
| rounds_per_week = int(input("How many rounds to reserve per week? : ") or 1) | |
| rounds_per_day = int(input("How many rounds to reserve per day? : ") or 1) | |
| if dow == "A" or dow == "a": | |
| dow = None | |
| else: | |
| dow = dow.split(",") | |
| if courses == 'A': | |
| courses = ",".join(list([f"{COURSEIDS_MAP[key]['id']}" for key in COURSEIDS_MAP.keys()])) | |
| else: | |
| courses = COURSEIDS_MAP[courses]['id'] | |
| else: | |
| # use defaults | |
| print("Loading defaults") | |
| starting_tee_time = defaults['starting_tee_time'] | |
| ending_tee_time = defaults['ending_tee_time'] | |
| courses = defaults['courses'] | |
| dow = defaults['dow'] | |
| num_players = defaults['num_players'] | |
| rounds_per_week = defaults['rounds_per_week'] | |
| rounds_per_day = defaults['rounds_per_day'] | |
| auto_reserve = defaults['auto_reserve'] | |
| if auto_reserve == 'y': | |
| AUTO_RESERVATION = True | |
| # Adjust for utc | |
| starting_tee_time += 4 | |
| ending_tee_time += 4 | |
| # get date range for next couple days | |
| base = datetime.datetime.today() | |
| numdays=10 | |
| date_list = [base + datetime.timedelta(days=x) for x in range(numdays)] | |
| # figure out tee times | |
| all_times = {} | |
| for date in date_list: | |
| #check filters | |
| if dow is None or str(date.isoweekday()) in dow: | |
| # get tee times | |
| date_str = date.strftime('%Y-%m-%d') | |
| print("Working on tee times for day: " + date_str) | |
| req = Request(f"https://phx-api-be-east-1b.kenna.io/tee-times?date={date_str}&courseIds={courses}") | |
| req.add_header('x-be-alias', 'essex-group') | |
| content = urlopen(req).read() | |
| info = json.loads(content) | |
| tee_times = [] | |
| for result in info: | |
| tee_times += result['teetimes'] | |
| newlist = sorted(tee_times, key=lambda d: datetime.datetime.strptime(d['teetime'], '%Y-%m-%dT%H:%M:%S.%fZ')) | |
| all_times[date_str]=newlist | |
| time.sleep(1) | |
| # filter for acceptable times | |
| final_tees = [] | |
| for dayslot in all_times: | |
| for timeslot in all_times[dayslot]: | |
| hour = datetime.datetime.strptime(timeslot['teetime'], '%Y-%m-%dT%H:%M:%S.%fZ').hour | |
| playerBookedCount = timeslot['bookedPlayers'] | |
| playerAvailCount = timeslot['maxPlayers'] | |
| if (hour >= starting_tee_time) \ | |
| and (hour < ending_tee_time) \ | |
| and playerAvailCount >= num_players: | |
| final_tees.append({"date":dayslot, | |
| "teetime": datetime.datetime.strptime(timeslot['teetime'], '%Y-%m-%dT%H:%M:%S.%fZ') - datetime.timedelta(hours=4), | |
| "playersBooked": playerBookedCount, | |
| "playersAvail": playerAvailCount, | |
| "course":timeslot['courseId'], | |
| "fee": timeslot['rates'][0]['greenFeeWalking'], | |
| "num_player_booking":num_players, | |
| "rounds_per_day":rounds_per_day, | |
| "rounds_per_week":rounds_per_week}) | |
| write_defaults({'starting_tee_time':starting_tee_time-4, 'ending_tee_time':ending_tee_time-4, 'courses':courses, 'dow':dow, 'num_players':num_players,"rounds_per_day":rounds_per_day, | |
| "rounds_per_week":rounds_per_week, 'auto_reserve':auto_reserve}) | |
| return final_tees | |
| def runner(): | |
| banner() | |
| final_tees = get_filters() | |
| if len(final_tees) > 0: | |
| print("Times Available") | |
| for idx,t in enumerate(final_tees): | |
| local_time = t['teetime'].astimezone(local_tz) | |
| course_name = list( | |
| filter(None, | |
| [COURSEIDS_MAP[c]['name'] if COURSEIDS_MAP[c]['id'] == t['course'] else '' for c in COURSEIDS_MAP] | |
| ) | |
| )[0] | |
| print(f"[{idx}] Date: {t['date'] } Time: {local_time.strftime('%H:%M %p')} Course: {course_name} Available: {t['playersAvail']} Fee: ${t['fee']/100}") | |
| do_reserve = 'n' | |
| if AUTO_RESERVATION is False: | |
| do_reserve = input("Proceed with reservation? , y for yes, n for no: ", "n") | |
| if AUTO_RESERVATION or do_reserve == "y": | |
| reserve(final_tees) | |
| if __name__ == "__main__": | |
| runner() |
Selling delays are one of the biggest frustrations homeowners face in traditional real estate transactions. Waiting for buyers, inspections, and loan approvals can extend the process for months.
Direct home buyers offer a faster solution by simplifying the entire selling process. Instead of listing a property and waiting for offers, homeowners can receive a fair cash proposal within a short period. This reduces uncertainty and speeds up decision-making.
Another advantage is the elimination of repair requirements. Many direct buyers purchase homes in their current condition, allowing sellers to avoid renovation costs and time-consuming upgrades.
Paperwork is also handled professionally, which prevents legal complications and ensures smoother transactions. Sellers benefit from clear timelines and predictable closing dates.
Overall, working with a direct home buyer helps homeowners avoid delays and complete property sales quickly and efficiently.
If you’re a homeowner looking for a quick and hassle-free way to sell your property, services like we buy houses Johnson city tn have become a popular solution. Instead of dealing with long listing times, expensive repairs, and uncertain buyer financing, many sellers now prefer direct home buyers who can provide fast cash offers and simple closing processes. These services are especially helpful for people facing relocation, financial challenges, inherited properties, or homes that need major repairs. In a competitive real estate market like Johnson City, working with a professional home-buying company can help you avoid delays, reduce stress, and complete your sale on a timeline that fits your needs.
Maintaining a beautiful landscape throughout the year requires seasonal planning. Each season presents unique opportunities to improve outdoor spaces.
In spring, homeowners should focus on planting flowers, fertilizing lawns, and preparing irrigation systems. This sets the foundation for healthy growth.
Summer maintenance includes watering, pruning, and pest control to protect plants from heat stress.
During fall, removing leaves, planting trees, and adding mulch helps prepare landscapes for colder months.
Winter landscaping involves protecting plants, maintaining pathways, and planning future upgrades.
Many homeowners search for landscaping services near me to receive professional guidance for seasonal maintenance.
Seasonal care ensures landscapes remain attractive and healthy year-round while reducing long-term maintenance challenges.