Corin1998 commited on
Commit
02c1f58
·
verified ·
1 Parent(s): 361c658

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +26 -36
app.py CHANGED
@@ -20,27 +20,29 @@ from pipelines.utils import detect_filetype, load_doc_text
20
  APP_TITLE = "候補者インテーク & レジュメ標準化(OpenAI版)"
21
 
22
 
23
- def process_resumes(files, candidate_id: str, additional_notes: str = ""):
24
- if not files:
25
  raise gr.Error("少なくとも1ファイルをアップロードしてください。")
26
 
27
  partial_records = []
28
  raw_texts = []
29
 
30
- for f in files:
31
- raw_bytes = f.read()
32
- filetype = detect_filetype(f.name, raw_bytes)
 
 
33
 
34
  # 1) テキスト抽出:画像/PDFはOpenAI Vision OCR、docx/txtは生文面+OpenAI整形
35
  if filetype in {"pdf", "image"}:
36
- text = extract_text_with_openai(raw_bytes, filename=f.name, filetype=filetype)
37
  else:
38
  base_text = load_doc_text(filetype, raw_bytes)
39
- text = extract_text_with_openai(base_text.encode("utf-8"), filename=f.name, filetype="txt")
40
 
41
- raw_texts.append({"filename": f.name, "text": text})
42
 
43
- # 2) OpenAIでセクション構造化 → ルールベース正規化
44
  structured = structure_with_openai(text)
45
  normalized = normalize_resume({
46
  "work_experience": structured.get("work_experience_raw", ""),
@@ -49,7 +51,7 @@ def process_resumes(files, candidate_id: str, additional_notes: str = ""):
49
  "skills": ", ".join(structured.get("skills_list", [])),
50
  })
51
  partial_records.append({
52
- "source": f.name,
53
  "text": text,
54
  "structured": structured,
55
  "normalized": normalized,
@@ -80,7 +82,7 @@ def process_resumes(files, candidate_id: str, additional_notes: str = ""):
80
  # 8) 構造化出力
81
  result_json = {
82
  "candidate_id": candidate_id or hashlib.sha256(merged_text.encode("utf-8")).hexdigest()[:16],
83
- "files": [f.name for f in files],
84
  "merged": merged,
85
  "skills": skills,
86
  "quality_score": score,
@@ -89,7 +91,7 @@ def process_resumes(files, candidate_id: str, additional_notes: str = ""):
89
  "notes": additional_notes,
90
  }
91
 
92
- # 9) HF Datasets 保存(任意)
93
  dataset_repo = os.environ.get("DATASET_REPO")
94
  commit_info = None
95
  if dataset_repo:
@@ -105,15 +107,14 @@ def process_resumes(files, candidate_id: str, additional_notes: str = ""):
105
 
106
  anon_pdf = (result_json["candidate_id"] + ".anon.pdf", anon_pdf_bytes)
107
 
108
- # ★ dictは全て文字列(JSON)化して返す(gr.JSONは使わない)
109
  return (
110
- json.dumps(result_json, ensure_ascii=False, indent=2), # 統合JSON(文字列)
111
- json.dumps(skills, ensure_ascii=False, indent=2), # スキル(文字列
112
- json.dumps(score, ensure_ascii=False, indent=2), # スコア(文字列)
113
- summaries.get("300chars", ""),
114
- summaries.get("100chars", ""),
115
- summaries.get("onesent", ""),
116
- anon_pdf, # (filename, bytes)
117
  json.dumps(commit_info or {"status": "skipped (DATASET_REPO not set)"}, ensure_ascii=False, indent=2),
118
  )
119
 
@@ -126,7 +127,7 @@ with gr.Blocks(title=APP_TITLE) as demo:
126
  label="レジュメ類 (PDF/画像/Word/テキスト) 複数可",
127
  file_count="multiple",
128
  file_types=[".pdf", ".png", ".jpg", ".jpeg", ".tiff", ".bmp", ".docx", ".txt"],
129
- type="file"
130
  )
131
  candidate_id = gr.Textbox(label="候補者ID(任意。未入力なら自動生成)")
132
  notes = gr.Textbox(label="補足メモ(任意)", lines=3)
@@ -137,11 +138,10 @@ with gr.Blocks(title=APP_TITLE) as demo:
137
  out_json = gr.Code(label="統合出力 (JSON)")
138
 
139
  with gr.Tab("抽出スキル"):
140
- # gr.JSON は使用しな
141
- out_skills = gr.Code(label="スキル一覧 (JSON)")
142
 
143
  with gr.Tab("品質スコア"):
144
- out_score = gr.Code(label="品質評価 (JSON)")
145
 
146
  with gr.Tab("要約 (300/100/1文)"):
147
  out_sum_300 = gr.Textbox(label="300字要約")
@@ -152,7 +152,7 @@ with gr.Blocks(title=APP_TITLE) as demo:
152
  out_pdf = gr.File(label="匿名PDFダウンロード")
153
 
154
  with gr.Tab("Datasets 保存ログ"):
155
- out_commit = gr.Code(label="コミット情報 (JSON)")
156
 
157
  run_btn.click(
158
  process_resumes,
@@ -162,14 +162,4 @@ with gr.Blocks(title=APP_TITLE) as demo:
162
 
163
 
164
  if __name__ == "__main__":
165
- # ヘッドレス/プロキシ/コンテナ環境対応
166
- server_name = os.getenv("GRADIO_SERVER_NAME", "0.0.0.0")
167
- server_port = int(os.getenv("PORT", os.getenv("GRADIO_SERVER_PORT", "7860")))
168
- share = os.getenv("GRADIO_SHARE", "false").lower() == "true"
169
- demo.launch(
170
- server_name=server_name,
171
- server_port=server_port,
172
- share=share,
173
- inbrowser=False,
174
- show_error=True,
175
- )
 
20
  APP_TITLE = "候補者インテーク & レジュメ標準化(OpenAI版)"
21
 
22
 
23
+ def process_resumes(filepaths, candidate_id: str, additional_notes: str = ""):
24
+ if not filepaths:
25
  raise gr.Error("少なくとも1ファイルをアップロードしてください。")
26
 
27
  partial_records = []
28
  raw_texts = []
29
 
30
+ for path in filepaths:
31
+ filename = os.path.basename(path)
32
+ with open(path, "rb") as f:
33
+ raw_bytes = f.read()
34
+ filetype = detect_filetype(filename, raw_bytes)
35
 
36
  # 1) テキスト抽出:画像/PDFはOpenAI Vision OCR、docx/txtは生文面+OpenAI整形
37
  if filetype in {"pdf", "image"}:
38
+ text = extract_text_with_openai(raw_bytes, filename=filename, filetype=filetype)
39
  else:
40
  base_text = load_doc_text(filetype, raw_bytes)
41
+ text = extract_text_with_openai(base_text.encode("utf-8"), filename=filename, filetype="txt")
42
 
43
+ raw_texts.append({"filename": filename, "text": text})
44
 
45
+ # 2) OpenAIでセクション構造化 → ルール正規化
46
  structured = structure_with_openai(text)
47
  normalized = normalize_resume({
48
  "work_experience": structured.get("work_experience_raw", ""),
 
51
  "skills": ", ".join(structured.get("skills_list", [])),
52
  })
53
  partial_records.append({
54
+ "source": filename,
55
  "text": text,
56
  "structured": structured,
57
  "normalized": normalized,
 
82
  # 8) 構造化出力
83
  result_json = {
84
  "candidate_id": candidate_id or hashlib.sha256(merged_text.encode("utf-8")).hexdigest()[:16],
85
+ "files": [os.path.basename(p) for p in filepaths],
86
  "merged": merged,
87
  "skills": skills,
88
  "quality_score": score,
 
91
  "notes": additional_notes,
92
  }
93
 
94
+ # 9) HF Datasets 保存
95
  dataset_repo = os.environ.get("DATASET_REPO")
96
  commit_info = None
97
  if dataset_repo:
 
107
 
108
  anon_pdf = (result_json["candidate_id"] + ".anon.pdf", anon_pdf_bytes)
109
 
 
110
  return (
111
+ json.dumps(result_json, ensure_ascii=False, indent=2),
112
+ json.dumps(skills, ensure_ascii=False, indent=2), # ← Code出力に合わせて文字列
113
+ json.dumps(score, ensure_ascii=False, indent=2),
114
+ summaries["300chars"],
115
+ summaries["100chars"],
116
+ summaries["onesent"],
117
+ anon_pdf,
118
  json.dumps(commit_info or {"status": "skipped (DATASET_REPO not set)"}, ensure_ascii=False, indent=2),
119
  )
120
 
 
127
  label="レジュメ類 (PDF/画像/Word/テキスト) 複数可",
128
  file_count="multiple",
129
  file_types=[".pdf", ".png", ".jpg", ".jpeg", ".tiff", ".bmp", ".docx", ".txt"],
130
+ type="filepath", # ← ここが重要('file' は不可)
131
  )
132
  candidate_id = gr.Textbox(label="候補者ID(任意。未入力なら自動生成)")
133
  notes = gr.Textbox(label="補足メモ(任意)", lines=3)
 
138
  out_json = gr.Code(label="統合出力 (JSON)")
139
 
140
  with gr.Tab("抽出スキル"):
141
+ out_skills = gr.Code(label="スキル一覧 (JSON)") # gr.JSON は型推論で落ちやすので Code に
 
142
 
143
  with gr.Tab("品質スコア"):
144
+ out_score = gr.Code(label="品質評価")
145
 
146
  with gr.Tab("要約 (300/100/1文)"):
147
  out_sum_300 = gr.Textbox(label="300字要約")
 
152
  out_pdf = gr.File(label="匿名PDFダウンロード")
153
 
154
  with gr.Tab("Datasets 保存ログ"):
155
+ out_commit = gr.Code(label="コミット情報")
156
 
157
  run_btn.click(
158
  process_resumes,
 
162
 
163
 
164
  if __name__ == "__main__":
165
+ demo.launch()