OppaAI's picture
Update app.py
d628d4c verified
raw
history blame
4.59 kB
import gradio as gr
from fastmcp import FastMCP
import logging
import requests
from datetime import datetime
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)
mcp = FastMCP("Jobicy Canada Remote Jobs Agent")
@mcp.tool(name="search_jobs")
def search_jobs_tool(query: str = "", location: str = "", limit: int = 20) -> dict:
base_url = "https://jobicy.com/api/v2/remote-jobs"
params = {}
if query.strip():
params["tag"] = query.strip()
if location.strip() and location.strip().lower() != "anywhere":
params["geo"] = location.strip().lower()
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/126.0.0.0 Safari/537.36"
}
try:
logger.info(f"Requesting Jobicy API with params: {params}")
response = requests.get(base_url, params=params, headers=headers, timeout=10)
response.raise_for_status()
data = response.json()
jobs_raw = data.get("jobs", [])
jobs = []
for job in jobs_raw:
salary_min = job.get("annualSalaryMin")
salary_max = job.get("annualSalaryMax")
currency = job.get("salaryCurrency", "")
if salary_min and salary_min != 0 and salary_max and salary_max != 0:
salary_str = f"{salary_min} - {salary_max} {currency}".strip()
else:
salary_str = "Not specified"
jobs.append({
"title": job.get("jobTitle", "No Title"),
"company": job.get("companyName", "Unknown Company"),
"location": job.get("jobGeo", "Remote"),
"url": job.get("url", "#"),
"pubDate": job.get("pubDate", "Unknown Date"),
"salary": salary_str
})
def parse_date(job):
try:
return datetime.fromisoformat(job["pubDate"].replace("Z", "+00:00"))
except Exception:
return datetime.min
jobs.sort(key=parse_date, reverse=True)
jobs = jobs[:limit]
logger.info(f"Found {len(jobs)} jobs from Jobicy after sorting.")
return {"jobs": jobs}
except requests.exceptions.HTTPError as http_err:
logger.error(f"HTTP error: {http_err}")
return {"error": f"HTTP error occurred: {http_err}"}
except requests.exceptions.Timeout:
logger.error("Request timed out.")
return {"error": "Request timed out. Please try again later."}
except requests.exceptions.RequestException as req_err:
logger.error(f"Request error: {req_err}")
return {"error": f"Request error: {req_err}"}
except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
return {"error": f"Unexpected error: {str(e)}"}
def search_jobs_ui(query="", location="", limit=10):
limit = int(max(1, min(limit, 50)))
result = search_jobs_tool(query, location, limit)
if "error" in result:
return f"❌ Error: {result['error']}"
jobs = result.get("jobs", [])
if not jobs:
return "No jobs found for your search. Try different keywords or location."
output = "# Jobicy Remote Job Search Results\n\n"
for i, job in enumerate(jobs, 1):
try:
posted_date = job['pubDate'][:10]
except Exception:
posted_date = job['pubDate']
output += (
f"### {i}. {job['title']}\n\n"
f"Company: {job['company']}\n\n"
f"Location: {job['location']}\n\n"
f"Salary: {job['salary']}\n\n"
f"Posted On: {posted_date}\n\n"
f"[Apply Here]({job['url']})\n\n"
"---\n\n"
)
return output
country_choices = [
"Anywhere", "canada", "united states", "united kingdom", "australia", "germany", "france", "india"
]
app = gr.Interface(
fn=search_jobs_ui,
inputs=[
gr.Textbox(label="Job Title / Keyword (optional)", placeholder="e.g., Software Engineer"),
gr.Dropdown(label="Country (optional)", choices=country_choices, value="Anywhere", interactive=True),
gr.Slider(minimum=1, maximum=50, value=10, step=1, label="Number of Results"),
],
outputs=gr.Markdown(),
title="Jobicy Remote Job Search",
description="Search remote jobs by optional job title and country, sorted by most recent postings.",
theme="huggingface"
)
if __name__ == "__main__":
app.launch(mcp_server=True)