OppaAI commited on
Commit
3c1606d
·
verified ·
1 Parent(s): a7b320c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +32 -18
app.py CHANGED
@@ -2,6 +2,7 @@ import gradio as gr
2
  from fastmcp import FastMCP
3
  import logging
4
  import requests
 
5
 
6
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
7
  logger = logging.getLogger(__name__)
@@ -9,11 +10,14 @@ logger = logging.getLogger(__name__)
9
  mcp = FastMCP("Jobicy Canada Remote Jobs Agent")
10
 
11
  @mcp.tool(name="search_jobs")
12
- def search_jobs_tool(query: str, location: str, limit: int = 10, salary: str = None, job_type: str = None) -> dict:
13
  base_url = "https://jobicy.com/api/v2/remote-jobs"
14
  params = {}
 
15
  if query.strip():
16
  params["tag"] = query.strip()
 
 
17
 
18
  headers = {
19
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
@@ -28,12 +32,12 @@ def search_jobs_tool(query: str, location: str, limit: int = 10, salary: str = N
28
  data = response.json()
29
 
30
  jobs_raw = data.get("jobs", [])
 
31
  jobs = []
32
- for job in jobs_raw[:limit]:
33
  salary_min = job.get("annualSalaryMin")
34
  salary_max = job.get("annualSalaryMax")
35
  currency = job.get("salaryCurrency", "")
36
- # 判斷salary有無實際數值(排除0或None)
37
  if salary_min and salary_min != 0 and salary_max and salary_max != 0:
38
  salary_str = f"{salary_min} - {salary_max} {currency}".strip()
39
  else:
@@ -48,7 +52,18 @@ def search_jobs_tool(query: str, location: str, limit: int = 10, salary: str = N
48
  "salary": salary_str
49
  })
50
 
51
- logger.info(f"Found {len(jobs)} jobs from Jobicy.")
 
 
 
 
 
 
 
 
 
 
 
52
  return {"jobs": jobs}
53
 
54
  except requests.exceptions.HTTPError as http_err:
@@ -64,21 +79,18 @@ def search_jobs_tool(query: str, location: str, limit: int = 10, salary: str = N
64
  logger.error(f"Unexpected error: {str(e)}")
65
  return {"error": f"Unexpected error: {str(e)}"}
66
 
67
- def search_jobs_ui(query, location, limit=10, salary=None, job_type=None):
68
- if not query:
69
- return "❌ Please provide a job title or keyword to search for."
70
-
71
  limit = int(max(1, min(limit, 50)))
72
- result = search_jobs_tool(query, location, limit, salary, job_type)
73
 
74
  if "error" in result:
75
  return f"❌ Error: {result['error']}"
76
 
77
  jobs = result.get("jobs", [])
78
  if not jobs:
79
- return "No jobs found for your search. Try different keywords."
80
 
81
- output = "# Jobicy Canada Remote Job Search Results\n\n"
82
  for i, job in enumerate(jobs, 1):
83
  output += (
84
  f"### {i}. {job['title']}\n"
@@ -88,21 +100,23 @@ def search_jobs_ui(query, location, limit=10, salary=None, job_type=None):
88
  f"**Posted On**: {job['pubDate']}\n"
89
  f"[Apply Here]({job['url']})\n\n"
90
  )
91
-
92
  return output
93
 
 
 
 
 
 
94
  app = gr.Interface(
95
  fn=search_jobs_ui,
96
  inputs=[
97
- gr.Textbox(label="Job Title / Keyword", placeholder="e.g., Software Engineer"),
98
- gr.Textbox(label="Location (ignored)", placeholder="Any, e.g., Toronto, ON"),
99
  gr.Slider(minimum=1, maximum=50, value=10, step=1, label="Number of Results"),
100
- gr.Textbox(label="Salary (optional, ignored)", placeholder="Not used"),
101
- gr.Textbox(label="Job Type (optional, ignored)", placeholder="Not used")
102
  ],
103
  outputs=gr.Markdown(),
104
- title="Jobicy Canada Remote Job Search",
105
- description="Search remote jobs in Canada using Jobicy API with FastMCP and Gradio.",
106
  theme="huggingface"
107
  )
108
 
 
2
  from fastmcp import FastMCP
3
  import logging
4
  import requests
5
+ from datetime import datetime
6
 
7
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
8
  logger = logging.getLogger(__name__)
 
10
  mcp = FastMCP("Jobicy Canada Remote Jobs Agent")
11
 
12
  @mcp.tool(name="search_jobs")
13
+ def search_jobs_tool(query: str = "", location: str = "", limit: int = 10) -> dict:
14
  base_url = "https://jobicy.com/api/v2/remote-jobs"
15
  params = {}
16
+
17
  if query.strip():
18
  params["tag"] = query.strip()
19
+ if location.strip():
20
+ params["geo"] = location.strip().lower()
21
 
22
  headers = {
23
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
 
32
  data = response.json()
33
 
34
  jobs_raw = data.get("jobs", [])
35
+
36
  jobs = []
37
+ for job in jobs_raw:
38
  salary_min = job.get("annualSalaryMin")
39
  salary_max = job.get("annualSalaryMax")
40
  currency = job.get("salaryCurrency", "")
 
41
  if salary_min and salary_min != 0 and salary_max and salary_max != 0:
42
  salary_str = f"{salary_min} - {salary_max} {currency}".strip()
43
  else:
 
52
  "salary": salary_str
53
  })
54
 
55
+ # 排序:由新到舊,嘗試用 pubDate ISO 格式解析,失敗排後面
56
+ def parse_date(job):
57
+ try:
58
+ return datetime.fromisoformat(job["pubDate"].replace("Z", "+00:00"))
59
+ except Exception:
60
+ return datetime.min
61
+
62
+ jobs.sort(key=parse_date, reverse=True)
63
+
64
+ jobs = jobs[:limit]
65
+
66
+ logger.info(f"Found {len(jobs)} jobs from Jobicy after sorting.")
67
  return {"jobs": jobs}
68
 
69
  except requests.exceptions.HTTPError as http_err:
 
79
  logger.error(f"Unexpected error: {str(e)}")
80
  return {"error": f"Unexpected error: {str(e)}"}
81
 
82
+ def search_jobs_ui(query="", location="", limit=10):
 
 
 
83
  limit = int(max(1, min(limit, 50)))
84
+ result = search_jobs_tool(query, location, limit)
85
 
86
  if "error" in result:
87
  return f"❌ Error: {result['error']}"
88
 
89
  jobs = result.get("jobs", [])
90
  if not jobs:
91
+ return "No jobs found for your search. Try different keywords or location."
92
 
93
+ output = "# Jobicy Remote Job Search Results\n\n"
94
  for i, job in enumerate(jobs, 1):
95
  output += (
96
  f"### {i}. {job['title']}\n"
 
100
  f"**Posted On**: {job['pubDate']}\n"
101
  f"[Apply Here]({job['url']})\n\n"
102
  )
 
103
  return output
104
 
105
+ country_choices = [
106
+ "", "canada", "united states", "united kingdom", "australia", "germany", "france", "india"
107
+ # 你可以再加多啲國家,或係由API提供清單動態生成
108
+ ]
109
+
110
  app = gr.Interface(
111
  fn=search_jobs_ui,
112
  inputs=[
113
+ gr.Textbox(label="Job Title / Keyword (optional)", placeholder="e.g., Software Engineer"),
114
+ gr.Dropdown(label="Country (optional)", choices=country_choices, value="", interactive=True),
115
  gr.Slider(minimum=1, maximum=50, value=10, step=1, label="Number of Results"),
 
 
116
  ],
117
  outputs=gr.Markdown(),
118
+ title="Jobicy Remote Job Search",
119
+ description="Search remote jobs by optional job title and country, sorted by most recent postings.",
120
  theme="huggingface"
121
  )
122