commit 8d02e35b5f7c73a16b964db688b691c260f2aede Author: Ahmed Hesham Date: Fri Sep 26 08:21:26 2025 +0300 Intial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5db0467 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +jobs.csv +.venv +__pycache__ \ No newline at end of file diff --git a/ai.py b/ai.py new file mode 100644 index 0000000..c408682 --- /dev/null +++ b/ai.py @@ -0,0 +1,68 @@ +# To run this code you need to install the following dependencies: +# pip install google-genai + +import base64 +import os +from google import genai +from google.genai import types + + +def generate(description, instruction): + client = genai.Client( + api_key="" + ) + + model = "gemini-2.5-flash" + contents = [ + types.Content( + role="user", + parts=[ + types.Part.from_text(text=description), + ], + ), + ] + generate_content_config = types.GenerateContentConfig( + thinking_config=types.ThinkingConfig( + thinking_budget=0, + ), + response_mime_type="application/json", + response_schema=genai.types.Schema( + type=genai.types.Type.OBJECT, + required=[ + "percentage", + "why I'm I a good fit", + "what I'm I missing", + ], + properties={ + "percentage": genai.types.Schema( + type=genai.types.Type.INTEGER, + ), + "why I'm I a good fit": genai.types.Schema( + type=genai.types.Type.STRING, + ), + "what I'm I missing": genai.types.Schema( + type=genai.types.Type.STRING, + ), + }, + ), + system_instruction=[ + types.Part.from_text(text=instruction), + ], + ) + + # for chunk in client.models.generate_content_stream( + # model=model, + # contents=contents, + # config=generate_content_config, + # ): + # print(chunk.text, end="") + response = client.models.generate_content( + model=model, + contents=contents, + config=generate_content_config, + ) + return response.text + + +if __name__ == "__main__": + generate() diff --git a/alert.py b/alert.py new file mode 100644 index 0000000..b4f2cb4 --- /dev/null +++ b/alert.py @@ -0,0 +1,48 @@ +import smtplib +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart + + +def send_email(sender, receiver, password, good_fit_jobs): + print("preparing message") + html_body = """ + +

My Job Alert

+

{count} jobs have been found that match your profile:

+ + + + """ + + message = MIMEText(html_body, "html") + message["Subject"] = f"MY JOB ALERT: {len(good_fit_jobs)} jobs has been found" + message["From"] = sender + message["To"] = receiver + + print(f"Sending email with {len(good_fit_jobs)} jobs") + with smtplib.SMTP("smtp.gmail.com", timeout=60, port=587) as connection: + connection.starttls() + connection.login(user=sender, password=password) + connection.sendmail( + from_addr=sender, + to_addrs=receiver, + msg=message.as_string(), + ) + connection.close() diff --git a/instruction.txt b/instruction.txt new file mode 100644 index 0000000..57205a1 --- /dev/null +++ b/instruction.txt @@ -0,0 +1,42 @@ +i will give you job descriptions and you tell me if i'm a good fit or not in a percentage, why I'm I a good fit and what I'm I missing +this is my cv: +Ahmed Hesham +Ahmed.hesham.farag@gmail.com +Cairo, Egypt +LinkedIn:https://www.linkedin.com/in/ahmed-hesham0/ +GitHub:https://github.com/ahmedhesham301 +PROFILE +DevOps Engineer with hands-on experience in automating CI/CD pipelines, containerizing +applications, and deploying scalable infrastructure on AWS using Docker, Terraform, and +Ansible. Skilled in backend development with Python and Go, building RESTful APIs and +automating system workflows. +EDUCATION +Sadat Academy | Cairo, Egypt +Computer science +Predicted Grade: Very good +Oct 2022 – June 2026 +TECHNICAL SKILLS +-DevOps Tools: Jenkins, Docker, Ansible, Terraform. +-Programming & Scripting: Python, GO, Bash, YAML, JSON. +-Monitoring & Observability: Prometheus, Grafana. +-Cloud Platforms & OS: Linux, AWS, Ubuntu. +-Database: SQL, PostgreSQL. +-Version control: Git, GitHub. +Projects +•Built CI/CD pipeline using Jenkins to test and deploy Flask app to EC2 on AWS. The +pipeline included building a Docker image, pushing it to Docker Hub, provisioning +infrastructure with Terraform, and configuring the server using Ansible. +GitHub Repo: weather-app +•A Docker image for a Minecraft server that allows you to specify the desired version as +an argument. The image automatically downloads the specified version from Mojang's +website and launches the server with a single command. GitHub repo +•An Amazon price tracker that monitors the price of a specified item. It scrapes the current +price and sends you an email notification whenever the price drops below your desired +price. +EXPERIENCE +Orange Digital Centre +Jan 2025 – Feb 2025 +DevOps internship +Gained hands-on experience in CI/CD pipelines using Jenkins, Ansible, and Terraform. +ADDITIONAL SKILLS +Languages: Arabic (Native), English (Fluent)Microsoft Office: Proficient in Word and PowerPoint \ No newline at end of file diff --git a/jobs.py b/jobs.py new file mode 100644 index 0000000..ba89908 --- /dev/null +++ b/jobs.py @@ -0,0 +1,30 @@ +from jobspy import scrape_jobs + + +def getJobs(jobTitle, results_wanted, hours_old): + jobs = scrape_jobs( + site_name=[ + "indeed", + "linkedin", + # "zip_recruiter", + "google", + # "glassdoor", + # "bayt", + # "naukri", + # "bdjobs", + ], + search_term=jobTitle, + location="Egypt", + results_wanted=results_wanted, + google_search_term=f"{jobTitle} jobs near Cairo since {hours_old} hours", + hours_old=hours_old, + country_indeed="Egypt", + linkedin_fetch_description=True, # gets more info such as description, direct job url (slower) + # proxies=["208.195.175.46:65095", "208.195.175.45:65095", "localhost"], + ) + print(f"Found {len(jobs)} jobs") + # print(jobs) + return jobs + # jobs.to_csv( + # "jobs.csv", quoting=csv.QUOTE_NONNUMERIC, escapechar="\\", index=False + # ) # to_excel diff --git a/main.py b/main.py new file mode 100644 index 0000000..f88f4cc --- /dev/null +++ b/main.py @@ -0,0 +1,68 @@ +from jobs import getJobs +from ai import generate +from alert import send_email +import json +import time +import logging + +SENDER = "" +PASSWORD = "" +RECEIVER = "" + +good_fit_jobs = [] + + +with open("instruction.txt", "r") as f: + CV = f.read() + + +def get_jobs(job_title, cv, results_wanted, hours_old): + jobs = getJobs(job_title, results_wanted, hours_old) + for i, job in jobs.iterrows(): + # print(job["description"]) + # print("_______________") + print("index is :", i) + + if (i + 1) % 10 == 0 and i != 0: + print("Sleeping to avoid API rate limits") + time.sleep(60) + try_count = 3 + while try_count > 0: + try: + cleaned_description = "\n".join( + [line for line in job["description"].splitlines() if line.strip()] + ) + ai_response = generate(cleaned_description, cv) + ai_response_dict = json.loads(ai_response) + break + except json.JSONDecodeError as e: + try_count -= 1 + print("_______________") + print(cleaned_description) + print("_______________") + print(ai_response) + print(e) + print("Sleeping after fail to avoid API rate limits") + time.sleep(6) + else: + print("All attempts failed.") + continue + # print(ai_response_dict) + if ai_response_dict["percentage"] > 0: + # print("adding job to good_fit_jobs") + good_fit_jobs.append( + { + "title": job["title"], + "url": job["job_url"], + "percentage": ai_response_dict["percentage"], + "why I'm I a good fit": ai_response_dict["why I'm I a good fit"], + "what I'm I missing": ai_response_dict["what I'm I missing"], + } + ) + + +if __name__ == "__main__": + get_jobs("devops", CV, results_wanted=2, hours_old=4) + # get_jobs("backend", CV, results_wanted=30, hours_old=2) + if len(good_fit_jobs) > 0: + send_email(SENDER, RECEIVER, PASSWORD, good_fit_jobs) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1dd3c07 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.11.0 +beautifulsoup4==4.13.5 +cachetools==5.5.2 +certifi==2025.8.3 +charset-normalizer==3.4.3 +google-auth==2.40.3 +google-genai==1.38.0 +h11==0.16.0 +httpcore==1.0.9 +httpx==0.28.1 +idna==3.10 +markdownify==0.13.1 +numpy==1.26.3 +pandas==2.3.2 +pyasn1==0.6.1 +pyasn1_modules==0.4.2 +pydantic==2.11.9 +pydantic_core==2.33.2 +python-dateutil==2.9.0.post0 +python-jobspy==1.1.82 +pytz==2025.2 +regex==2024.11.6 +requests==2.32.5 +rsa==4.9.1 +six==1.17.0 +sniffio==1.3.1 +soupsieve==2.8 +tenacity==9.1.2 +tls-client==1.0.1 +typing-inspection==0.4.1 +typing_extensions==4.15.0 +tzdata==2025.2 +urllib3==2.5.0 +websockets==15.0.1