Corin1998 commited on
Commit
3d28506
·
verified ·
1 Parent(s): a6c79cf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +31 -38
app.py CHANGED
@@ -17,37 +17,31 @@ from pipelines.scoring import compute_quality_score
17
  from pipelines.storage import persist_to_hf
18
  from pipelines.utils import detect_filetype, load_doc_text
19
 
20
- # ❶ app.py の該当箇所を以下に置き換え
21
-
22
- # ...(importsはそのまま)
23
-
24
  APP_TITLE = "候補者インテーク & レジュメ標準化(OpenAI版)"
25
 
26
- def process_resumes(filepaths, candidate_id: str, additional_notes: str = ""):
27
- import os, json, hashlib, gradio as gr
28
- if not filepaths:
29
  raise gr.Error("少なくとも1ファイルをアップロードしてください。")
30
 
31
  partial_records = []
32
  raw_texts = []
33
 
34
- for path in filepaths:
35
- filename = os.path.basename(path)
36
- with open(path, "rb") as f:
37
- raw_bytes = f.read()
38
 
39
- filetype = detect_filetype(filename, raw_bytes)
40
-
41
- # 1) テキスト抽出
42
  if filetype in {"pdf", "image"}:
43
- text = extract_text_with_openai(raw_bytes, filename=filename, filetype=filetype)
44
  else:
45
  base_text = load_doc_text(filetype, raw_bytes)
46
- text = extract_text_with_openai(base_text.encode("utf-8"), filename=filename, filetype="txt")
 
47
 
48
- raw_texts.append({"filename": filename, "text": text})
49
 
50
- # 2) 構造化3) 正規化
51
  structured = structure_with_openai(text)
52
  normalized = normalize_resume({
53
  "work_experience": structured.get("work_experience_raw", ""),
@@ -56,16 +50,16 @@ def process_resumes(filepaths, candidate_id: str, additional_notes: str = ""):
56
  "skills": ", ".join(structured.get("skills_list", [])),
57
  })
58
  partial_records.append({
59
- "source": filename,
60
  "text": text,
61
  "structured": structured,
62
  "normalized": normalized,
63
  })
64
 
65
- # 4) 統合
66
  merged = merge_normalized_records([r["normalized"] for r in partial_records])
67
 
68
- # 5) スキル抽出
69
  merged_text = "\n\n".join([r["text"] for r in partial_records])
70
  skills = extract_skills(merged_text, {
71
  "work_experience": merged.get("raw_sections", {}).get("work_experience", ""),
@@ -74,20 +68,20 @@ def process_resumes(filepaths, candidate_id: str, additional_notes: str = ""):
74
  "skills": ", ".join(merged.get("skills", [])),
75
  })
76
 
77
- # 6) 匿名化
78
  anonymized_text, anon_map = anonymize_text(merged_text)
79
  anon_pdf_bytes = render_anonymized_pdf(anonymized_text)
80
 
81
- # 7) 品質スコア
82
  score = compute_quality_score(merged_text, merged)
83
 
84
- # 8) 要約
85
  summaries = summarize_with_openai(merged_text)
86
 
87
- # 9) 構造化出力(UIバグ回避のため **すべて文字列** で返す)
88
  result_json = {
89
  "candidate_id": candidate_id or hashlib.sha256(merged_text.encode("utf-8")).hexdigest()[:16],
90
- "files": [os.path.basename(p) for p in filepaths],
91
  "merged": merged,
92
  "skills": skills,
93
  "quality_score": score,
@@ -96,7 +90,7 @@ def process_resumes(filepaths, candidate_id: str, additional_notes: str = ""):
96
  "notes": additional_notes,
97
  }
98
 
99
- # 10) HF Datasets 保存
100
  dataset_repo = os.environ.get("DATASET_REPO")
101
  commit_info = None
102
  if dataset_repo:
@@ -113,12 +107,12 @@ def process_resumes(filepaths, candidate_id: str, additional_notes: str = ""):
113
  anon_pdf = (result_json["candidate_id"] + ".anon.pdf", anon_pdf_bytes)
114
 
115
  return (
116
- json.dumps(result_json, ensure_ascii=False, indent=2), # out_json -> Code(str)
117
- json.dumps(skills, ensure_ascii=False, indent=2), # out_skills -> Code(str)
118
- json.dumps(score, ensure_ascii=False, indent=2), # out_score -> Code(str)
119
- summaries["300chars"],
120
- summaries["100chars"],
121
- summaries["onesent"],
122
  anon_pdf,
123
  json.dumps(commit_info or {"status": "skipped (DATASET_REPO not set)"}, ensure_ascii=False, indent=2),
124
  )
@@ -132,7 +126,7 @@ with gr.Blocks(title=APP_TITLE) as demo:
132
  label="レジュメ類 (PDF/画像/Word/テキスト) 複数可",
133
  file_count="multiple",
134
  file_types=[".pdf", ".png", ".jpg", ".jpeg", ".tiff", ".bmp", ".docx", ".txt"],
135
- type="filepath", # ← ここが重要:'file' は非対応・例外の元
136
  )
137
  candidate_id = gr.Textbox(label="候補者ID(任意。未入力なら自動生成)")
138
  notes = gr.Textbox(label="補足メモ(任意)", lines=3)
@@ -143,7 +137,7 @@ with gr.Blocks(title=APP_TITLE) as demo:
143
  out_json = gr.Code(label="統合出力 (JSON文字列)")
144
 
145
  with gr.Tab("抽出スキル"):
146
- out_skills = gr.Code(label="スキル一覧 (JSON文字列)") # gr.JSON を **使わない**
147
 
148
  with gr.Tab("品質スコア"):
149
  out_score = gr.Code(label="品質評価 (JSON文字列)")
@@ -165,7 +159,6 @@ with gr.Blocks(title=APP_TITLE) as demo:
165
  outputs=[out_json, out_skills, out_score, out_sum_300, out_sum_100, out_sum_1, out_pdf, out_commit],
166
  )
167
 
 
168
  if __name__ == "__main__":
169
- # ローカル(gradio.live)想定なら share=True が安全
170
- # Spaces 内では demo.launch() のままでもOK
171
- demo.launch(share=True, server_name="0.0.0.0")
 
17
  from pipelines.storage import persist_to_hf
18
  from pipelines.utils import detect_filetype, load_doc_text
19
 
 
 
 
 
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
+ # 生テキストをOpenAIへ渡し、整形本文を返す
40
+ text = extract_text_with_openai(base_text.encode("utf-8"), filename=f.name, filetype="txt")
41
 
42
+ raw_texts.append({"filename": f.name, "text": text})
43
 
44
+ # 2) OpenAIでセクション構造化ルールベース正規化
45
  structured = structure_with_openai(text)
46
  normalized = normalize_resume({
47
  "work_experience": structured.get("work_experience_raw", ""),
 
50
  "skills": ", ".join(structured.get("skills_list", [])),
51
  })
52
  partial_records.append({
53
+ "source": f.name,
54
  "text": text,
55
  "structured": structured,
56
  "normalized": normalized,
57
  })
58
 
59
+ # 3) 統合(複数ファイル→1候補者)
60
  merged = merge_normalized_records([r["normalized"] for r in partial_records])
61
 
62
+ # 4) スキル抽出
63
  merged_text = "\n\n".join([r["text"] for r in partial_records])
64
  skills = extract_skills(merged_text, {
65
  "work_experience": merged.get("raw_sections", {}).get("work_experience", ""),
 
68
  "skills": ", ".join(merged.get("skills", [])),
69
  })
70
 
71
+ # 5) 匿名化
72
  anonymized_text, anon_map = anonymize_text(merged_text)
73
  anon_pdf_bytes = render_anonymized_pdf(anonymized_text)
74
 
75
+ # 6) 品質スコア
76
  score = compute_quality_score(merged_text, merged)
77
 
78
+ # 7) 要約(300/100/1文)
79
  summaries = summarize_with_openai(merged_text)
80
 
81
+ # 8) 構造化出力(UIバグ回避のため **すべて文字列** で返す)
82
  result_json = {
83
  "candidate_id": candidate_id or hashlib.sha256(merged_text.encode("utf-8")).hexdigest()[:16],
84
+ "files": [f.name for f in files],
85
  "merged": merged,
86
  "skills": skills,
87
  "quality_score": score,
 
90
  "notes": additional_notes,
91
  }
92
 
93
+ # 9) HF Datasets 保存
94
  dataset_repo = os.environ.get("DATASET_REPO")
95
  commit_info = None
96
  if dataset_repo:
 
107
  anon_pdf = (result_json["candidate_id"] + ".anon.pdf", anon_pdf_bytes)
108
 
109
  return (
110
+ json.dumps(result_json, ensure_ascii=False, indent=2), # JSON全体
111
+ json.dumps(skills, ensure_ascii=False, indent=2), # skillsは文字列で返す
112
+ json.dumps(score, ensure_ascii=False, indent=2), # スコアも文字列
113
+ summaries.get("300chars", ""),
114
+ summaries.get("100chars", ""),
115
+ summaries.get("onesent", ""),
116
  anon_pdf,
117
  json.dumps(commit_info or {"status": "skipped (DATASET_REPO not set)"}, ensure_ascii=False, indent=2),
118
  )
 
126
  label="レジュメ類 (PDF/画像/Word/テキスト) 複数可",
127
  file_count="multiple",
128
  file_types=[".pdf", ".png", ".jpg", ".jpeg", ".tiff", ".bmp", ".docx", ".txt"],
129
+ type="file" # fileオブジェクトを受ける(process_resumesの実装に合致)
130
  )
131
  candidate_id = gr.Textbox(label="候補者ID(任意。未入力なら自動生成)")
132
  notes = gr.Textbox(label="補足メモ(任意)", lines=3)
 
137
  out_json = gr.Code(label="統合出力 (JSON文字列)")
138
 
139
  with gr.Tab("抽出スキル"):
140
+ out_skills = gr.Code(label="スキル一覧 (JSON文字列)") # gr.JSON を使わない
141
 
142
  with gr.Tab("品質スコア"):
143
  out_score = gr.Code(label="品質評価 (JSON文字列)")
 
159
  outputs=[out_json, out_skills, out_score, out_sum_300, out_sum_100, out_sum_1, out_pdf, out_commit],
160
  )
161
 
162
+
163
  if __name__ == "__main__":
164
+ demo.launch()