Corin1998 commited on
Commit
894c91e
·
verified ·
1 Parent(s): 2418fb0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +31 -109
app.py CHANGED
@@ -1,25 +1,20 @@
1
- import os
2
- import io
3
- import json
4
- import hashlib
5
- import gradio as gr
6
-
7
- from pipelines.openai_ingest import (
8
- extract_text_with_openai,
9
- structure_with_openai,
10
- summarize_with_openai,
11
- )
12
- from pipelines.parsing import normalize_resume
13
- from pipelines.merge import merge_normalized_records
14
- from pipelines.skills import extract_skills
15
- from pipelines.anonymize import anonymize_text, render_anonymized_pdf
16
- 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_TITLE = "候補者インテーク & レジュメ標準化(OpenAI版)"
21
 
22
 
 
23
  def process_resumes(files, candidate_id: str, additional_notes: str = ""):
24
  if not files:
25
  raise gr.Error("少なくとも1ファイルをアップロードしてください。")
@@ -27,18 +22,21 @@ def process_resumes(files, candidate_id: str, additional_notes: str = ""):
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) テキスト抽出
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) 構造化
44
  structured = structure_with_openai(text)
@@ -49,38 +47,18 @@ 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,
56
  })
57
 
58
- # 3) 統合
59
- merged = merge_normalized_records([r["normalized"] for r in partial_records])
60
-
61
- # 4) スキル抽出
62
- merged_text = "\n\n".join([r["text"] for r in partial_records])
63
- skills = extract_skills(merged_text, {
64
- "work_experience": merged.get("raw_sections", {}).get("work_experience", ""),
65
- "education": merged.get("raw_sections", {}).get("education", ""),
66
- "certifications": merged.get("raw_sections", {}).get("certifications", ""),
67
- "skills": ", ".join(merged.get("skills", [])),
68
- })
69
-
70
- # 5) 匿名化
71
- anonymized_text, anon_map = anonymize_text(merged_text)
72
- anon_pdf_bytes = render_anonymized_pdf(anonymized_text)
73
-
74
- # 6) 品質スコア
75
- score = compute_quality_score(merged_text, merged)
76
-
77
- # 7) 要約
78
- summaries = summarize_with_openai(merged_text)
79
 
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,25 +67,12 @@ 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:
96
- file_hash = result_json["candidate_id"]
97
- commit_info = persist_to_hf(
98
- dataset_repo=dataset_repo,
99
- record=result_json,
100
- anon_pdf_bytes=anon_pdf_bytes,
101
- parquet_path=f"candidates/{file_hash}.parquet",
102
- json_path=f"candidates/{file_hash}.json",
103
- pdf_path=f"candidates/{file_hash}.anon.pdf",
104
- )
105
-
106
- anon_pdf = (result_json["candidate_id"] + ".anon.pdf", anon_pdf_bytes)
107
 
 
108
  return (
109
  json.dumps(result_json, ensure_ascii=False, indent=2),
110
- json.dumps(skills, ensure_ascii=False, indent=2),
111
  json.dumps(score, ensure_ascii=False, indent=2),
112
  summaries["300chars"],
113
  summaries["100chars"],
@@ -117,49 +82,6 @@ def process_resumes(files, candidate_id: str, additional_notes: str = ""):
117
  )
118
 
119
 
120
- with gr.Blocks(title=APP_TITLE) as demo:
121
- gr.Markdown(f"# {APP_TITLE}\n複数ファイルを統合→OpenAIで読み込み/構造化/要約→匿名化→Datasets保存")
122
-
123
- with gr.Row():
124
- in_files = gr.Files(
125
- label="レジュメ類 (PDF/画像/Word/テキスト) 複数可",
126
- file_count="multiple",
127
- file_types=[".pdf", ".png", ".jpg", ".jpeg", ".tiff", ".bmp", ".docx", ".txt"],
128
- type="file"
129
- )
130
- candidate_id = gr.Textbox(label="候補者ID(任意。未入力なら自動生成)")
131
- notes = gr.Textbox(label="補足メモ(任意)", lines=3)
132
-
133
- run_btn = gr.Button("実行")
134
-
135
- with gr.Tab("構造化JSON"):
136
- out_json = gr.Code(label="統合出力 (JSON)")
137
-
138
- with gr.Tab("抽出スキル"):
139
- # Gradio 4.44.0 の schema まわりを避けるため JSON 表示は Code に
140
- out_skills = gr.Code(label="スキル一覧(JSON)")
141
-
142
- with gr.Tab("品質スコア"):
143
- out_score = gr.Code(label="品質評価(JSON)")
144
-
145
- with gr.Tab("要約 (300/100/1文)"):
146
- out_sum_300 = gr.Textbox(label="300字要約")
147
- out_sum_100 = gr.Textbox(label="100字要約")
148
- out_sum_1 = gr.Textbox(label="1文要約")
149
-
150
- with gr.Tab("匿名PDF"):
151
- out_pdf = gr.File(label="匿名PDFダウンロード")
152
-
153
- with gr.Tab("Datasets 保存ログ"):
154
- out_commit = gr.Code(label="コミット情報(JSON)")
155
-
156
- run_btn.click(
157
- process_resumes,
158
- inputs=[in_files, candidate_id, notes],
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
- # HF Spaces での公開実行(localhost アクセス不可対策)
165
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
+ # --- 変更1: Files の type を "filepath" にし、ハンドラをファイルパス対応へ ---
2
+ with gr.Row():
3
+ in_files = gr.Files(
4
+ label="レジュメ類 (PDF/画像/Word/テキスト) 複数可",
5
+ file_count="multiple",
6
+ file_types=[".pdf", ".png", ".jpg", ".jpeg", ".tiff", ".bmp", ".docx", ".txt"],
7
+ type="filepath", # "file" から修正
8
+ )
9
+ candidate_id = gr.Textbox(label="候補者ID(任意。未入力なら自動生成)")
10
+
11
+
12
+ # --- 変更2: out_skills は schema 周りの不具合回避のため JSON → Code に ---
13
+ with gr.Tab("抽出スキル"):
14
+ out_skills = gr.Code(label="スキル一覧(JSON)") # gr.JSON から修正
 
 
 
 
 
 
15
 
16
 
17
+ # --- 変更3: ハンドラの files 取り扱いを「パス」前提に修正 ---
18
  def process_resumes(files, candidate_id: str, additional_notes: str = ""):
19
  if not files:
20
  raise gr.Error("少なくとも1ファイルをアップロードしてください。")
 
22
  partial_records = []
23
  raw_texts = []
24
 
25
+ for path in files: # ← UploadedFile ではなく filepath の配列
26
+ with open(path, "rb") as fh:
27
+ raw_bytes = fh.read()
28
+ fname = os.path.basename(path)
29
+
30
+ filetype = detect_filetype(fname, raw_bytes)
31
 
32
  # 1) テキスト抽出
33
  if filetype in {"pdf", "image"}:
34
+ text = extract_text_with_openai(raw_bytes, filename=fname, filetype=filetype)
35
  else:
36
  base_text = load_doc_text(filetype, raw_bytes)
37
+ text = extract_text_with_openai(base_text.encode("utf-8"), filename=fname, filetype="txt")
38
 
39
+ raw_texts.append({"filename": fname, "text": text})
40
 
41
  # 2) 構造化
42
  structured = structure_with_openai(text)
 
47
  "skills": ", ".join(structured.get("skills_list", [])),
48
  })
49
  partial_records.append({
50
+ "source": fname,
51
  "text": text,
52
  "structured": structured,
53
  "normalized": normalized,
54
  })
55
 
56
+ # ...(中略:ロジックはそのまま)...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
  # 8) まとめ
59
  result_json = {
60
  "candidate_id": candidate_id or hashlib.sha256(merged_text.encode("utf-8")).hexdigest()[:16],
61
+ "files": [os.path.basename(p) for p in files], # ← fname 配列へ
62
  "merged": merged,
63
  "skills": skills,
64
  "quality_score": score,
 
67
  "notes": additional_notes,
68
  }
69
 
70
+ # ...(Datasets 保存処理はそのまま)...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
+ # 返り値:out_skills を Code にしたので JSON 文字列で返す
73
  return (
74
  json.dumps(result_json, ensure_ascii=False, indent=2),
75
+ json.dumps(skills, ensure_ascii=False, indent=2), # ← 文字列化
76
  json.dumps(score, ensure_ascii=False, indent=2),
77
  summaries["300chars"],
78
  summaries["100chars"],
 
82
  )
83
 
84
 
85
+ # --- 変更4: HF Spaces での起動を安定させるため launch の引数を明示 ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  if __name__ == "__main__":
 
87
  demo.launch(server_name="0.0.0.0", server_port=7860)