Initial commit. Project constructed without git as a standalone intially.

This commit is contained in:
tsb95 2026-02-18 09:01:45 -10:00
commit 6ad4908689
4 changed files with 211 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.env
/__pycache__
*.log

41
get_message.py Normal file
View File

@ -0,0 +1,41 @@
from openai import OpenAI
import os
from dotenv import load_dotenv
load_dotenv()
OPEN_AI_API_KEY = os.getenv("OPEN_AI_API_KEY")
client = OpenAI(api_key=OPEN_AI_API_KEY)
example_format = """
Hello NAME_OF_JOB_POSTER,
I see you're looking to get some support with WHAT_THEY_NEED_HELP_WITH, and I would be more than happy to help. My name is Taylor and I have helped many students succeed in TOPIC, and THEIR_SPECIFIC_TOPIC in particular can be tough. I'd be happy to provide intuitive, relatable explanations for these difficult concepts. When would you be available for an initial session?
I look forward to working with you!
Warm Regards,
Taylor
"""
"Things about me to consider include that I have a degree in mathematics, have worked as a data analyst and software developer, have over a decade of experience working as a tutor, teacher, and classroom instructor. I have extensive experiene with all level of mathematics, general computer science, html, python, javascript, physics, R, Jupyter, Data Science, Statistics, SAT, ACT, and more."
system_content = f"""
"You are an assistan whose goal it is to create a personalized message for a prospective client of a tutor based off a given job description, subject, and name of job poster. The job may or may not be posted by the student themselves. Your response should address the person who posted the job and take in relevant context from the job description to craft a meaningful message that will showcase the ability and excitement of the tutor to help the student with whatever the job description mentions. Some relevant information about the tutor is that I have a degree in mathematics, have worked as a data analyst and software developer, have over a decade of experience working as a tutor, teacher, and classroom instructor. I have extensive experiene with all level of mathematics, general computer science, html, python, javascript, physics, R, Jupyter, Data Science, Statistics, SAT, ACT, and more. A sample message that has been used to some success and can be used as a general template is as follows. {example_format}"
"""
def generate_custom_message(job_description, job_poster, subject):
completion = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": system_content},
{
"role": "user",
"content": f"Create a message based on the following. \n Job Description: {job_description} \n Job Poster: {job_poster} \n Subject: {subject}"
}
]
)
return completion.choices[0].message.content

138
main.py Normal file
View File

@ -0,0 +1,138 @@
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
from get_message import generate_custom_message
import logging
import random
from logging.handlers import RotatingFileHandler
import os
from dotenv import load_dotenv
load_dotenv()
# Constants for login
USERNAME = os.getenv("USERNAME")
PASSWORD = os.getenv("PASSWORD")
# Set up logging
# Set up rotating log handler
handler = RotatingFileHandler('./wyzant_logs.log', maxBytes=5000000, backupCount=5) # 5 MB per log file, keeps 5 backups
logging.basicConfig(
handlers=[handler],
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logging.info("Starting script...")
REFRESH_WAIT_TIME = 120
REFRESH_VARIANCE = 15
# Selenium Config
chrome_driver_path = "/usr/local/bin/chromedriver/chromedriver"
service = Service(executable_path=chrome_driver_path)
driver = webdriver.Chrome(service=service)
# Go to login site
logging.info("Navigating to login page...")
driver.get("https://www.wyzant.com/login")
# Login
time.sleep(3)
username_input = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//form[@class='sso-login-form']//input[@id='Username']"))
)
username_input.send_keys(USERNAME)
password_input = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//form[@class='sso-login-form']//input[@id='Password']"))
)
password_input.send_keys(PASSWORD)
logging.info("Entered username and password...")
login_button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//form[@class='sso-login-form']//button[@type='submit']"))
)
login_button.click()
logging.info("Clicked login button.")
# Wait and click jobs link
time.sleep(3)
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//div[@class='profile-image round-photo']"))
)
job_count_element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//div[@data-templateid='jobCountTemplate']"))
)
job_count_element.click()
logging.info("Clicked jobs list link button.")
# Loop over the jobs list until no more jobs are found
while True:
try:
# Wait for the jobs list to load and locate the first job link
time.sleep(2)
first_job_description = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "#jobs-list .job-description"))
)
first_job_link = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "#jobs-list .row.spc-small-ew .job-details-link"))
)
first_student = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "#jobs-list .text-semibold.spc-zero-n.spc-tiny-s"))
)
# Extract job info for GPT
job_description_text = first_job_description.text
job_subject_text = first_job_link.text
job_student_text = first_student.text
# Generate and send message
message = generate_custom_message(job_description_text, job_student_text, job_subject_text)
logging.info(f"Generated custom message for {job_student_text}, subject: {job_subject_text}.")
first_job_link.click()
# Wait for the message text area and send the message
time.sleep(3)
text_area = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "personal_message"))
)
text_area.clear() # Clears the text area if there's any existing text
text_area.send_keys(message) # Insert your message
# Check if the hourly rate checkbox exists and click it if present
try:
checkbox = WebDriverWait(driver, 2).until(
EC.presence_of_element_located((By.ID, "agree_partner_hourly_rate"))
)
checkbox.click()
logging.info("Clicked rate agreement checkbox.")
except:
logging.info("No rate agreement checkbox found.")
# Submit the application
submit_button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "input.btn.old-button-color"))
)
submit_button.click()
logging.info("Application submitted successfully.")
# Wait for the page to redirect to the jobs list again
time.sleep(3)
except Exception as e:
logging.error(f"Failed find a job to apply. Waiting 1 mins")
delay = random.uniform(REFRESH_WAIT_TIME - REFRESH_VARIANCE, REFRESH_WAIT_TIME + REFRESH_VARIANCE)
time.sleep(60)
driver.refresh()
time.sleep(10)

29
requirements.txt Normal file
View File

@ -0,0 +1,29 @@
annotated-types==0.7.0
anyio==4.6.0
attrs==24.2.0
beautifulsoup4==4.12.3
certifi==2024.8.30
distro==1.9.0
exceptiongroup==1.2.2
h11==0.14.0
httpcore==1.0.6
httpx==0.27.2
idna==3.10
jiter==0.5.0
openai==1.51.0
outcome==1.3.0.post0
pydantic==2.9.2
pydantic_core==2.23.4
PySocks==1.7.1
python-dotenv==1.2.1
selenium==4.25.0
sniffio==1.3.1
sortedcontainers==2.4.0
soupsieve==2.6
tqdm==4.66.5
trio==0.26.2
trio-websocket==0.11.1
typing_extensions==4.12.2
urllib3==2.2.3
websocket-client==1.8.0
wsproto==1.2.0