-
-
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() |
How do you pull the course ids?
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"}
I’ll be honest—my house needed work. The foundation had shifted, there was water damage in the basement, and the kitchen hadn’t been updated in decades. I thought I’d have to spend tens of thousands just to list it.
But then I heard about Favor Home Solutions from someone who’d sold in similar condition. They explained I could sell without touching a thing. I was skeptical at first, but they walked me through the process and showed me how it could work.
And it did. I sold my home as-is, no repairs, no contractors, and no out-of-pocket expenses. I never thought that was even possible. If your house has seen better days and you’re not up for fixing it all, I’d suggest looking into what Favor Home Solutions can do.
Visit Them: https://favorhomesolutions.com/tennessee-washington-county-johnson-city-sell-house-for-cash//
Edging adds structure and definition to gardens and lawns, making your landscaping look polished and well-maintained. Traditional edging materials include brick, stone, and metal, but there are plenty of creative alternatives. Wooden logs or reclaimed railroad ties add rustic charm, while recycled glass or colorful tiles bring a unique artistic touch. Living edging — such as low-growing plants like lavender, thyme, or boxwood — provides a soft, natural border. Curved edging lines can make gardens feel more organic, while straight lines create a clean, formal look. Edging also prevents grass from invading flower beds and makes mowing easier. By choosing a style that complements your home and garden design, you can transform your landscape with this simple yet effective upgrade.
Searching for “landscaping near me”? We provide professional, reliable landscaping services designed to make your outdoor space both beautiful and functional. Our team specializes in everything from lawn care and garden design to patios, walkways, and seasonal cleanups, all tailored to your property’s unique needs.
Stone patios and paver pathways are timeless additions that elevate any home. Miller’s Landscaping takes pride in designing and building them to perfection. Their team helps homeowners choose the right materials, colors, and patterns that match their home’s architecture. Many people searching for “landscapers near me” discover that Miller’s provides unmatched expertise and craftsmanship. As one of the leading providers of Landscaping Services in Sewell, NJ, they’ve transformed countless properties with elegant hardscaping features. Their patios are built to last, combining durability with design flair. Whether you’re upgrading your outdoor space or starting fresh, Miller’s Landscaping offers everything you need to create stunning, lasting results.
Dealing with an inherited house comes with emotional and financial stress. Mine sat vacant for months before I knew what to do. After researching, I found Travis Buys Homes, which buys inherited properties without repairs or cleaning.
In Charlotte, many families choose we buy houses Charlotte NC when dealing with probate or inherited homes. Services like sell my house fast Charlotte NC help them close fast and avoid months of upkeep.
In Statesville, families who inherit older properties often call we buy houses Statesville NC, especially when the home needs major updates. Sell my house fast Statesville NC helps them divide assets quickly.
Gastonia sellers face the same issues — inherited homes are often outdated. Companies like we buy houses Gastonia NC provide quick, fair options. Many use sell my house fast Gastonia NC to avoid the long listing process.
If you inherited a property you don’t want to maintain, cash buyers make the process simple.
Parents often wonder whether an in-person Chicago Home Tutor is better than online tutoring. Both have benefits, but home tutoring offers stronger engagement and fewer distractions.
In-person sessions allow tutors to observe body language, track focus, and adjust teaching techniques instantly. Students also tend to stay more committed when someone is physically present.
However, online tutoring is great for quick sessions and flexibility. Many families combine both.
For daily Homework Help in Chicago, in-person tutoring is usually more effective because students get immediate assistance with assignments and classroom materials.
The best choice depends on your child’s learning style and schedule.
Many homeowners who want a modern outdoor look reach out to Millers Landscaping because professionals understand the latest design trends. Some of the most popular trends today include minimalist plant layouts, clean stone borders, LED accent lighting, and mixed-material patios. Native plants and drought-resistant landscaping are also gaining popularity as homeowners look for low-maintenance options. Modern designs often focus on symmetry, open spaces, and functional outdoor living areas like lounges, fire pits, and outdoor kitchens. A professional landscaping team can incorporate these trends into a polished, contemporary design that fits your home perfectly.
Uh oh!
There was an error while loading. Please reload this page.