Spaces:
Sleeping
Sleeping
| 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") | |
| 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) | |