高橋慧 commited on
Commit
d08530d
·
1 Parent(s): c308431
Files changed (3) hide show
  1. README.md +90 -60
  2. app.py +264 -264
  3. requirements.txt +3 -3
README.md CHANGED
@@ -4,102 +4,132 @@ emoji: 🏥
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: gradio
7
- sdk_version: 4.36.1
8
  app_file: app.py
9
  pinned: false
10
  license: mit
11
  ---
12
 
13
- # 🏥 臨床試験適格性評価システム(完全版)
14
 
15
  このアプリケーションは患者情報に基づいて適切な臨床試験を見つけ、AIエージェントが適格性を自動評価するシステムです。
16
 
17
  ## ✨ 主要機能
18
 
19
- ### 🔍 リアルタイム臨床試験検索
20
- - **ClinicalTrials.gov API**: 日本で実施中の最新臨床試験を自動検索
21
- - **多言語対応**: 日本語入力 → 英語検索 → 日本語結果
22
- - **条件絞り込み**: 腫瘍タイプ、募集状況、実施地域での自動フィルタリング
23
 
24
- ### 🤖 AI適格性評価システム
25
- - **LangChain + Groq**: 高速で正確な自然言語処理
26
- - **3段階評価**: Yes(適格)/ No(不適格)/ Unclear(要検討)
27
- - **詳細判断理由**: AI が各試験への適格性を詳細に説明
28
 
29
- ### 💾 データ管理機能
30
- - **インタラクティブフィルタリング**: 適格性レベルでの結果絞り込み
31
- - **CSV エクスポート**: フィルタ済み/全データのダウンロード
32
- - **リアルタイム更新**: 最新の臨床試験情報を常時取得
 
 
 
33
 
34
  ## 🚀 使用方法
35
 
36
- ### 1. 環境設定
37
- **Settings Variables and secrets** で以下のAPIキーを設定:
 
 
 
 
 
38
 
 
 
39
  ```
40
- GROQ_API_KEY: あなたのGroq APIキー(必須)
41
  OPENAI_API_KEY: あなたのOpenAI APIキー(オプション)
42
  ```
43
 
44
- ### 2. 患者情報入力
45
- - **基本情報**: 年齢、性別、腫瘍タイプ
46
- - **詳細情報**: 遺伝子変異、測定可能腫瘍、生検可能性
47
 
48
- ### 3. 検索・評価実行
49
- - 「🔍 Generate Clinical Trials Data (Real API)」をクリック
50
- - AIが自動的に適格性を評価
 
51
 
52
- ### 4. 結果の活用
53
- - **フィルタリング**: ✅適格 / ❌不適格 / ❓要検討 別に表示
54
- - **詳細確認**: 各試験の判断理由を確認
55
- - **データ保存**: 必要に応じてCSVでダウンロード
56
 
57
- ## 🛠️ 技術スタック
 
 
 
58
 
59
- ### フロントエンド
60
- - **Gradio 4.36.1**: 直感的なWebインターフェース
61
- - **Pandas**: データ処理・表示
62
 
63
- ### AI/機械学習
64
- - **LangChain**: AIエージェント構築フレームワーク
65
- - **Groq API**: 高速LLM推論(Llama3-70B)
66
- - **OpenAI API**: 補完的なLLM機能(オプション)
 
67
 
68
- ### データソース
69
- - **ClinicalTrials.gov API**: 国際的な臨床試験データベース
70
- - **リアルタイム検索**: 最新の募集状況を反映
 
 
71
 
72
- ## 📊 システム動作モード
 
 
 
73
 
74
- ### 完全版モード(API連携有効)
75
- - 環境変数が正しく設定されている場合
76
- - 実際のClinicalTrials.govからリアルタイムデータ取得
77
- - AI による適格性自動評価
78
 
79
- ### ⚠️ 制限モード(API制限あり)
80
- - 一部の環境変数が未設定の場合
81
- - 基本的な検索機能のみ利用可能
 
82
 
83
- ### 🔧 軽量版モード(サンプルデータ)
84
- - 依存関係エラーが発生した場合
85
- - サンプルデータでの機能デモンストレーション
86
 
87
- ## 🔐 プライバシー・セキュリティ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
- - **患者情報**: ローカル処理のみ、サーバーに保存されません
90
- - **API通信**: HTTPS暗号化通信
91
- - **データ匿名化**: 個人識別情報は使用されません
92
 
93
- ## 📝 ライセンス
 
 
94
 
95
- MIT License - 学術・商用利用可能
 
 
96
 
97
- ## 🙋‍♂️ サポート
 
 
98
 
99
- 問題が発生した場合:
100
- 1. **ログ確認**: Spaces の Logs タブでエラー詳細を確認
101
- 2. **環境変数**: API キーが正しく設定されているか確認
102
- 3. **リフレッシュ**: ページを再読み込みしてリトライ
103
 
104
  ---
105
 
 
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: gradio
7
+ sdk_version: 4.20.1
8
  app_file: app.py
9
  pinned: false
10
  license: mit
11
  ---
12
 
13
+ # 🏥 臨床試験適格性評価システム
14
 
15
  このアプリケーションは患者情報に基づいて適切な臨床試験を見つけ、AIエージェントが適格性を自動評価するシステムです。
16
 
17
  ## ✨ 主要機能
18
 
19
+ ### 🔍 段階的機能提供
20
+ システムは利用可能な依存関係に応じて、自動的に適切なモードで動作します:
 
 
21
 
22
+ #### 🟢 完全版モード
23
+ - **条件**: 全ての依存関係 + API キー設定済み
24
+ - **機能**: リアルタイム検索 + AI適格性評価 + データエクスポート
 
25
 
26
+ #### 🟡 基本版モード
27
+ - **条件**: 基本依存関係のみ + API キー設定済み
28
+ - **機能**: ClinicalTrials.gov検索 + 基本評価 + データエクスポート
29
+
30
+ #### 🔴 軽量版モード
31
+ - **条件**: Gradio + Pandas のみ
32
+ - **機能**: サンプルデータ表示 + フィルタリング + UI確認
33
 
34
  ## 🚀 使用方法
35
 
36
+ ### 1. 基本操作
37
+ 1. **患者情報入力**: 年齢、性別、腫瘍タイプを入力
38
+ 2. **詳細情報**: 遺伝子変異、測定可能腫瘍の有無を選択
39
+ 3. **検索実行**: 対応するボタンをクリック
40
+ 4. **結果確認**: HTMLテーブルで結果を確認
41
+ 5. **フィルタリング**: ✅適格 / ❌不適格 / ❓要検討 で絞り込み
42
+ 6. **データ保存**: CSV形式でダウンロード
43
 
44
+ ### 2. 環境設定(完全版利用時)
45
+ **Settings → Variables and secrets** で設定:
46
  ```
47
+ GROQ_API_KEY: あなたのGroq APIキー
48
  OPENAI_API_KEY: あなたのOpenAI APIキー(オプション)
49
  ```
50
 
51
+ ## 🛠️ 技術仕様
 
 
52
 
53
+ ### UI フレームワーク
54
+ - **Gradio 4.20.1**: 安定版LTSバージョン
55
+ - **HTMLテーブル**: カスタムCSS + レスポンシブデザイン
56
+ - **段階的フォールバック**: 依存関係エラー耐性
57
 
58
+ ### データソース
59
+ - **ClinicalTrials.gov API**: リアルタイム臨床試験データ
60
+ - **日本限定検索**: 募集中の日本国内実施試験のみ
 
61
 
62
+ ### AI/機械学習
63
+ - **LangChain**: エージェント構築フレームワーク
64
+ - **Groq**: 高速LLM推論(Llama3-70B)
65
+ - **OpenAI**: 補完的LLM機能
66
 
67
+ ## 📊 表示機能
 
 
68
 
69
+ ### インタラクティブテーブル
70
+ - **色分け表示**: 適格性レベルごとの背景色
71
+ - **リンク機能**: NCTIDクリックで公式ページへ
72
+ - **統計表示**: 適格/不適格/要検討の件数集計
73
+ - **レスポンシブ**: モバイル対応デザイン
74
 
75
+ ### フィルタリング
76
+ - **✅ 適格**: 参加可能な試験のみ表示
77
+ - **❌ 不適格**: 参加不可能な試験のみ表示
78
+ - **❓ 要検討**: 追加情報が必要な試験のみ表示
79
+ - **📊 全件**: フィルタなしで全件表示
80
 
81
+ ### データエクスポート
82
+ - **CSV形式**: Excel等で開ける形式
83
+ - **UTF-8 with BOM**: 日本語文字化け防止
84
+ - **全データ保持**: フィルタリング状態関係なく全情報保存
85
 
86
+ ## 🔐 プライバシー・セキュリティ
 
 
 
87
 
88
+ - **ローカル処理**: 患者情報はサーバーに保存されません
89
+ - **HTTPS通信**: API通信は暗号化
90
+ - **匿名化**: 個人識別情報は使用されません
91
+ - **一時データ**: セッション終了で全データ削除
92
 
93
+ ## 📝 システム要件
 
 
94
 
95
+ ### 最小要件(軽量版)
96
+ ```
97
+ gradio==4.20.1
98
+ numpy==1.21.6
99
+ pandas==1.3.5
100
+ requests==2.31.0
101
+ ```
102
+
103
+ ### 完全版要件
104
+ ```
105
+ + langchain ecosystem
106
+ + openai / groq
107
+ + SQLAlchemy
108
+ + pydantic
109
+ + tiktoken
110
+ + tenacity
111
+ ```
112
+
113
+ ## 🙋‍♂️ トラブルシューティング
114
 
115
+ ### よくある問題
 
 
116
 
117
+ 1. **起動時エラー**
118
+ - 段階的フォールバックにより軽量版で動作
119
+ - システム状態を確認して必要な依存関係を追加
120
 
121
+ 2. **API接続エラー**
122
+ - 環境変数設定を確認
123
+ - APIキーの有効性を確認
124
 
125
+ 3. **検索結果なし**
126
+ - 腫瘍タイプの英語表記を確認
127
+ - より一般的な用語で再検索
128
 
129
+ ### サポート
130
+ - **ログ確認**: Spaces の Logs タブ
131
+ - **リフレッシュ**: ページ再読み込み
132
+ - **段階的診断**: システム状態表示を確認
133
 
134
  ---
135
 
app.py CHANGED
@@ -113,249 +113,234 @@ def fetch_clinical_trials_basic(cancer_name):
113
  "Eligibility Criteria": eligibilityCriteria
114
  })
115
 
116
- return pd.DataFrame(data_list)
117
  else:
118
  print(f"API呼び出し失敗: {response.status_code}")
119
- return pd.DataFrame()
120
 
121
  except Exception as e:
122
  print(f"基本API呼び出しエラー: {e}")
123
- return pd.DataFrame()
124
-
125
- # エラーハンドリング付きでエージェント評価を実行する関数
126
- def evaluate_with_retry(agent, criteria, question, max_retries=3):
127
- """エラーハンドリング付きでエージェント評価を実行"""
128
- if agent is None:
129
- return "評価エラー: エージェントが初期化されていません。API keyを確認してください。"
130
-
131
- for attempt in range(max_retries):
132
- try:
133
- return agent.evaluate_eligibility(criteria, question)
134
- except Exception as e:
135
- if "missing variables" in str(e):
136
- print(f"プロンプトテンプレートエラー (試行 {attempt + 1}/{max_retries}): {e}")
137
- return "評価エラー: プロンプトテンプレートの設定に問題があります"
138
- elif "no healthy upstream" in str(e) or "InternalServerError" in str(e):
139
- print(f"Groqサーバーエラー (試行 {attempt + 1}/{max_retries}): {e}")
140
- if attempt < max_retries - 1:
141
- time.sleep(2)
142
- continue
143
- else:
144
- return "評価エラー: サーバーに接続できませんでした"
145
- elif "API key" in str(e) or "authentication" in str(e).lower():
146
- return "評価エラー: API keyが無効または設定されていません"
147
- else:
148
- print(f"予期しないエラー (試行 {attempt + 1}/{max_retries}): {e}")
149
- if attempt < max_retries - 1:
150
- time.sleep(1)
151
- continue
152
- else:
153
- return f"評価エラー: {str(e)}"
154
- return "評価エラー: 最大リトライ回数に達しました"
155
-
156
- def evaluate_grade_with_retry(agent, judgment, max_retries=3):
157
- """エラーハンドリング付きでグレード評価を実行"""
158
- if agent is None:
159
- return "unclear"
160
-
161
- for attempt in range(max_retries):
162
- try:
163
- return agent.evaluate_eligibility(judgment)
164
- except Exception as e:
165
- if "no healthy upstream" in str(e) or "InternalServerError" in str(e):
166
- print(f"Groqサーバーエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}")
167
- if attempt < max_retries - 1:
168
- time.sleep(2)
169
- continue
170
- else:
171
- return "unclear"
172
- elif "API key" in str(e) or "authentication" in str(e).lower():
173
- return "unclear"
174
- else:
175
- print(f"予期しないエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}")
176
- if attempt < max_retries - 1:
177
- time.sleep(1)
178
- continue
179
- else:
180
- return "unclear"
181
- return "unclear"
182
 
183
  # 軽量版データ生成関数
184
- def generate_sample_dataframe(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable):
185
- """サンプルデータを生成(軽量版)"""
186
  try:
187
  if not all([age, sex, tumor_type]):
188
- return pd.DataFrame()
189
 
190
- sample_data = {
191
- 'NCTID': ['NCT12345678', 'NCT87654321', 'NCT11111111'],
192
- 'AgentGrade': ['yes', 'no', 'unclear'],
193
- 'Title': [
194
- f'Clinical Trial for {tumor_type} in {sex} patients',
195
- f'Alternative treatment for {tumor_type}',
196
- f'Experimental therapy for {tumor_type} with {GeneMutation}'
197
- ],
198
- 'AgentJudgment': [
199
- f'{age}歳{sex}の{tumor_type}患者は参加可能です。詳細な検査結果により最終判断が必要です。',
200
- f'{age}歳{sex}の{tumor_type}患者は年齢制限により参加できません。',
201
- f'{age}歳{sex}の{tumor_type}患者の参加は追加情報が必要で不明確です。'
202
- ],
203
- 'Japanes Locations': ['Tokyo, Osaka', 'Kyoto, Fukuoka', 'Nagoya, Sendai'],
204
- 'Primary Completion Date': ['2025-12-31', '2026-06-30', '2025-09-15'],
205
- 'Cancer': [tumor_type, tumor_type, tumor_type],
206
- 'Summary': [
207
- f'Phase II study evaluating new treatment for {tumor_type}',
208
- f'Comparative study of standard vs experimental therapy for {tumor_type}',
209
- f'Early phase trial testing combination therapy for {tumor_type}'
210
- ],
211
- 'Eligibility Criteria': [
212
- f'Age 18-75, confirmed {tumor_type}, adequate organ function',
213
- f'Age 20-65, {tumor_type} with specific mutations, ECOG 0-1',
214
- f'Age 18-80, advanced {tumor_type}, previous treatment failure'
215
- ]
216
- }
 
 
 
 
 
 
 
 
217
 
218
- return pd.DataFrame(sample_data)
219
 
220
  except Exception as e:
221
  print(f"サンプルデータ生成エラー: {e}")
222
- return pd.DataFrame()
223
 
224
  # 基本版データ生成関数(ClinicalTrials.gov API使用、AI評価なし)
225
- def generate_basic_dataframe(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable):
226
  """基本版のデータ生成(API使用、AI評価なし)"""
227
  try:
228
  if not all([age, sex, tumor_type]):
229
- return pd.DataFrame()
230
 
231
  # 実際のAPI呼び出し
232
- df = fetch_clinical_trials_basic(tumor_type)
233
 
234
- if df.empty:
235
  print("臨床試験データが見つかりませんでした")
236
- return pd.DataFrame()
237
-
238
- # AI評価なしのプレースホルダー
239
- df['AgentJudgment'] = f'基本版:{age}歳{sex}の{tumor_type}患者への詳細評価にはAI機能が必要です'
240
- df['AgentGrade'] = 'unclear'
241
 
242
- # 列を指定した順に並び替え
243
- columns_order = ['NCTID', 'AgentGrade', 'Title', 'AgentJudgment', 'Japanes Locations',
244
- 'Primary Completion Date', 'Cancer', 'Summary', 'Eligibility Criteria']
 
245
 
246
- # 存在する列のみを選択
247
- available_columns = [col for col in columns_order if col in df.columns]
248
- df = df[available_columns]
249
-
250
- print(f"基本版評価完了。結果: {len(df)} 件")
251
- return df
252
 
253
  except Exception as e:
254
- print(f"基本版データフレーム生成中に予期しないエラー: {e}")
255
  traceback.print_exc()
256
- return pd.DataFrame()
257
 
258
- # 完全版データ生成関数
259
- def generate_full_dataframe(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable):
260
- """完全版のデータ生成(実際のAPI使用 + AI評価���"""
261
- try:
262
- if not all([age, sex, tumor_type]):
263
- return pd.DataFrame()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
- # 日本語の腫瘍タイプを英語に翻訳
266
- try:
267
- if translator is not None:
268
- TumorName = translator.translate(tumor_type)
269
- print(f"腫瘍タイプ翻訳: {tumor_type} → {TumorName}")
270
- else:
271
- print("翻訳エージェントが利用できません。元の値を使用します。")
272
- TumorName = tumor_type
273
- except Exception as e:
274
- print(f"翻訳エラー: {e}")
275
- TumorName = tumor_type
276
-
277
- # 質問文を生成
278
- try:
279
- ex_question = generate_ex_question_English(age, sex, TumorName, GeneMutation, Meseable, Biopsiable)
280
- print(f"生成された質問: {ex_question}")
281
- except Exception as e:
282
- print(f"質問生成エラー: {e}")
283
- return pd.DataFrame()
284
 
285
- # 臨床試験データの取得
286
- try:
287
- print(f"臨床試験データを検索中: {TumorName}")
288
- df = fetch_clinical_trials(TumorName)
289
- if df.empty:
290
- print("臨床試験データが見つかりませんでした")
291
- return pd.DataFrame()
292
- print(f"取得した臨床試験数: {len(df)}")
293
- except Exception as e:
294
- print(f"臨床試験データ取得エラー: {e}")
295
- return pd.DataFrame()
296
 
297
- df['AgentJudgment'] = None
298
- df['AgentGrade'] = None
 
 
299
 
300
- # 臨床試験の適格性の評価
301
- NCTIDs = list(df['NCTID'])
 
302
 
303
- for i, nct_id in enumerate(NCTIDs):
304
- try:
305
- print(f"評価中 ({i+1}/{len(NCTIDs)}): {nct_id}")
306
- target_criteria = df.loc[df['NCTID'] == nct_id, 'Eligibility Criteria'].values[0]
307
-
308
- # エラーハンドリング付きで評価実行
309
- agent_judgment = evaluate_with_retry(CriteriaCheckAgent, target_criteria, ex_question)
310
- agent_grade = evaluate_grade_with_retry(grader_agent, agent_judgment)
311
-
312
- # データフレームの更新
313
- df.loc[df['NCTID'] == nct_id, 'AgentJudgment'] = agent_judgment
314
- df.loc[df['NCTID'] == nct_id, 'AgentGrade'] = agent_grade
315
-
316
- except Exception as e:
317
- print(f"NCTID {nct_id} の評価中にエラー: {e}")
318
- df.loc[df['NCTID'] == nct_id, 'AgentJudgment'] = f"エラー: {str(e)}"
319
- df.loc[df['NCTID'] == nct_id, 'AgentGrade'] = "unclear"
320
 
321
- # 列を指定した順に並び替え
322
- columns_order = ['NCTID', 'AgentGrade', 'Title', 'AgentJudgment', 'Japanes Locations',
323
- 'Primary Completion Date', 'Cancer', 'Summary', 'Eligibility Criteria']
324
 
325
- # 存在する列のみを選択
326
- available_columns = [col for col in columns_order if col in df.columns]
327
- df = df[available_columns]
328
 
329
- print(f"完全版評価完了。結果: {len(df)} 件")
330
- return df
 
331
 
332
- except Exception as e:
333
- print(f"完全版データフレーム生成中に予期しないエラー: {e}")
334
- traceback.print_exc()
335
- return pd.DataFrame()
336
-
337
- # CSVとして保存しダウンロードする関数
338
- def download_filtered_csv(df):
339
- try:
340
- if df is None or len(df) == 0:
341
- return None
342
- file_path = "filtered_data.csv"
343
- df.to_csv(file_path, index=False)
344
- return file_path
345
- except Exception as e:
346
- print(f"CSV保存エラー: {e}")
347
- return None
 
 
 
 
 
 
 
348
 
349
- def download_full_csv(df):
 
 
 
 
 
350
  try:
351
- if df is None or len(df) == 0:
352
- return None
353
- file_path = "full_data.csv"
354
- df.to_csv(file_path, index=False)
355
- return file_path
356
  except Exception as e:
357
- print(f"CSV保存エラー: {e}")
358
- return None
359
 
360
  # システム状態の確認
361
  def get_system_status():
@@ -369,6 +354,25 @@ def get_system_status():
369
  else:
370
  return "🔴 軽量版", "サンプルデータのみ表示"
371
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
372
  # Gradioインターフェースの作成
373
  with gr.Blocks(title="臨床試験適格性評価", theme=gr.themes.Soft()) as demo:
374
  gr.Markdown("## 🏥 臨床試験適格性評価インターフェース")
@@ -399,16 +403,11 @@ with gr.Blocks(title="臨床試験適格性評価", theme=gr.themes.Soft()) as d
399
  measurable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Measurable Tumor", value="有り")
400
  biopsiable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Biopsiable Tumor", value="有り")
401
 
402
- # データフレーム表示エリア
403
- dataframe_output = gr.DataFrame(
404
- label="Clinical Trials Results",
405
- interactive=False,
406
- wrap=True
407
- )
408
 
409
- # 内部状態用の非表示コンポーネント
410
- original_df_state = gr.State(value=None)
411
- filtered_df_state = gr.State(value=None)
412
 
413
  # ボタン類
414
  with gr.Row():
@@ -426,97 +425,98 @@ with gr.Blocks(title="臨床試験適格性評価", theme=gr.themes.Soft()) as d
426
  all_button = gr.Button("📊 Show All Trials", variant="secondary")
427
 
428
  with gr.Row():
429
- download_filtered_button = gr.Button("💾 Download Filtered Data")
430
- download_full_button = gr.Button("💾 Download Full Data")
431
 
432
  # ダウンロードファイル
433
- download_filtered_output = gr.File(label="Download Filtered Data", visible=False)
434
- download_full_output = gr.File(label="Download Full Data", visible=False)
435
 
436
  # プログレス表示
437
  progress_text = gr.Textbox(label="Processing Status", value="Ready", interactive=False)
438
 
439
  # イベントハンドリング
440
- def update_dataframe_and_state(age, sex, tumor_type, gene_mutation, measurable, biopsiable):
441
- """データフレーム生成と状態更新"""
442
  try:
443
  if FULL_VERSION:
444
- progress_text.value = "🔍 実際の臨床試験データを検索中(AI評価付き)..."
445
- df = generate_full_dataframe(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
 
446
  elif LANGCHAIN_AVAILABLE:
447
- progress_text.value = "📡 ClinicalTrials.govから基本データを検索中..."
448
- df = generate_basic_dataframe(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
449
  else:
450
- progress_text.value = "📋 サンプルデータを生成中..."
451
- df = generate_sample_dataframe(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
452
 
453
- if len(df) > 0:
454
- progress_text.value = f"✅ 完了: {len(df)} 件の臨床試験が見つかりました"
 
455
  else:
456
- progress_text.value = "⚠️ 該当する臨床試験が見つかりませんでした"
 
457
 
458
- return df, df, df, progress_text.value
459
  except Exception as e:
460
  error_msg = f"❌ エラー: {str(e)}"
461
- print(f"データフレーム更新エラー: {e}")
462
- return pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), error_msg
 
463
 
464
- def filter_and_update(original_df, grade):
465
  """フィルタリングと表示更新"""
466
- if original_df is None or len(original_df) == 0:
467
- return original_df, original_df
468
-
469
  try:
470
- if grade == "all":
471
- df_filtered = original_df
472
- else:
473
- df_filtered = original_df[original_df['AgentGrade'] == grade]
474
- return df_filtered, df_filtered
475
  except Exception as e:
476
  print(f"フィルタリングエラー: {e}")
477
- return original_df, original_df
478
 
479
- # ボタン動作の設定
 
 
 
 
 
 
 
 
 
 
480
  generate_button.click(
481
- fn=update_dataframe_and_state,
482
  inputs=[age_input, sex_input, tumor_type_input, gene_mutation_input, measurable_input, biopsiable_input],
483
- outputs=[dataframe_output, original_df_state, filtered_df_state, progress_text]
484
  )
485
 
486
  yes_button.click(
487
- fn=lambda df: filter_and_update(df, "yes"),
488
- inputs=[original_df_state],
489
- outputs=[dataframe_output, filtered_df_state]
490
  )
491
 
492
  no_button.click(
493
- fn=lambda df: filter_and_update(df, "no"),
494
- inputs=[original_df_state],
495
- outputs=[dataframe_output, filtered_df_state]
496
  )
497
 
498
  unclear_button.click(
499
- fn=lambda df: filter_and_update(df, "unclear"),
500
- inputs=[original_df_state],
501
- outputs=[dataframe_output, filtered_df_state]
502
  )
503
 
504
  all_button.click(
505
- fn=lambda df: filter_and_update(df, "all"),
506
- inputs=[original_df_state],
507
- outputs=[dataframe_output, filtered_df_state]
508
- )
509
-
510
- download_filtered_button.click(
511
- fn=download_filtered_csv,
512
- inputs=[filtered_df_state],
513
- outputs=[download_filtered_output]
514
  )
515
 
516
- download_full_button.click(
517
- fn=download_full_csv,
518
- inputs=[original_df_state],
519
- outputs=[download_full_output]
520
  )
521
 
522
  # フッター情報
 
113
  "Eligibility Criteria": eligibilityCriteria
114
  })
115
 
116
+ return data_list
117
  else:
118
  print(f"API呼び出し失敗: {response.status_code}")
119
+ return []
120
 
121
  except Exception as e:
122
  print(f"基本API呼び出しエラー: {e}")
123
+ return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
  # 軽量版データ生成関数
126
+ def generate_sample_data(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable):
127
+ """サンプルデータを生成(辞書のリスト形式)"""
128
  try:
129
  if not all([age, sex, tumor_type]):
130
+ return []
131
 
132
+ sample_data = [
133
+ {
134
+ "NCTID": "NCT12345678",
135
+ "AgentGrade": "yes",
136
+ "Title": f"Clinical Trial for {tumor_type} in {sex} patients",
137
+ "AgentJudgment": f"{age}歳{sex}の{tumor_type}患者は参加可能です。詳細な検査結果により最終判断が必要です。",
138
+ "Japanes Locations": "Tokyo, Osaka",
139
+ "Primary Completion Date": "2025-12-31",
140
+ "Cancer": tumor_type,
141
+ "Summary": f"Phase II study evaluating new treatment for {tumor_type}",
142
+ "Eligibility Criteria": f"Age 18-75, confirmed {tumor_type}, adequate organ function"
143
+ },
144
+ {
145
+ "NCTID": "NCT87654321",
146
+ "AgentGrade": "no",
147
+ "Title": f"Alternative treatment for {tumor_type}",
148
+ "AgentJudgment": f"{age}歳{sex}の{tumor_type}患者は年齢制限により参加できません。",
149
+ "Japanes Locations": "Kyoto, Fukuoka",
150
+ "Primary Completion Date": "2026-06-30",
151
+ "Cancer": tumor_type,
152
+ "Summary": f"Comparative study of standard vs experimental therapy for {tumor_type}",
153
+ "Eligibility Criteria": f"Age 20-65, {tumor_type} with specific mutations, ECOG 0-1"
154
+ },
155
+ {
156
+ "NCTID": "NCT11111111",
157
+ "AgentGrade": "unclear",
158
+ "Title": f"Experimental therapy for {tumor_type} with {GeneMutation}",
159
+ "AgentJudgment": f"{age}歳{sex}の{tumor_type}患者の参加は追加情報が必要で不明確です。",
160
+ "Japanes Locations": "Nagoya, Sendai",
161
+ "Primary Completion Date": "2025-09-15",
162
+ "Cancer": tumor_type,
163
+ "Summary": f"Early phase trial testing combination therapy for {tumor_type}",
164
+ "Eligibility Criteria": f"Age 18-80, advanced {tumor_type}, previous treatment failure"
165
+ }
166
+ ]
167
 
168
+ return sample_data
169
 
170
  except Exception as e:
171
  print(f"サンプルデータ生成エラー: {e}")
172
+ return []
173
 
174
  # 基本版データ生成関数(ClinicalTrials.gov API使用、AI評価なし)
175
+ def generate_basic_data(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable):
176
  """基本版のデータ生成(API使用、AI評価なし)"""
177
  try:
178
  if not all([age, sex, tumor_type]):
179
+ return []
180
 
181
  # 実際のAPI呼び出し
182
+ data_list = fetch_clinical_trials_basic(tumor_type)
183
 
184
+ if not data_list:
185
  print("臨床試験データが見つかりませんでした")
186
+ return []
 
 
 
 
187
 
188
+ # AI評価なしのプレースホルダーを追加
189
+ for item in data_list:
190
+ item['AgentJudgment'] = f'基本版:{age}歳{sex}の{tumor_type}患者への詳細評価にはAI機能が必要です'
191
+ item['AgentGrade'] = 'unclear'
192
 
193
+ print(f"基本版評価完了。結果: {len(data_list)} 件")
194
+ return data_list
 
 
 
 
195
 
196
  except Exception as e:
197
+ print(f"基本版データ生成中に予期しないエラー: {e}")
198
  traceback.print_exc()
199
+ return []
200
 
201
+ # HTMLテーブル生成関数
202
+ def create_html_table(data, show_grade=True):
203
+ """データをHTMLテーブルに変換"""
204
+ if not data:
205
+ return "<div style='text-align: center; padding: 20px; color: #666;'>📄 データがありません</div>"
206
+
207
+ # CSS スタイル
208
+ table_style = """
209
+ <style>
210
+ .clinical-table {
211
+ width: 100%;
212
+ border-collapse: collapse;
213
+ margin: 10px 0;
214
+ font-family: Arial, sans-serif;
215
+ font-size: 14px;
216
+ }
217
+ .clinical-table th {
218
+ background-color: #f8f9fa;
219
+ border: 1px solid #dee2e6;
220
+ padding: 12px 8px;
221
+ text-align: left;
222
+ font-weight: bold;
223
+ color: #495057;
224
+ }
225
+ .clinical-table td {
226
+ border: 1px solid #dee2e6;
227
+ padding: 10px 8px;
228
+ vertical-align: top;
229
+ }
230
+ .grade-yes { background-color: #d4edda; }
231
+ .grade-no { background-color: #f8d7da; }
232
+ .grade-unclear { background-color: #fff3cd; }
233
+ .clinical-table tr:hover {
234
+ background-color: #f5f5f5;
235
+ }
236
+ .nctid-link {
237
+ color: #007bff;
238
+ text-decoration: none;
239
+ font-weight: bold;
240
+ }
241
+ .nctid-link:hover {
242
+ text-decoration: underline;
243
+ }
244
+ .title-cell {
245
+ max-width: 300px;
246
+ word-wrap: break-word;
247
+ }
248
+ .criteria-cell {
249
+ max-width: 400px;
250
+ word-wrap: break-word;
251
+ font-size: 12px;
252
+ }
253
+ </style>
254
+ """
255
+
256
+ # テーブルヘッダー
257
+ html = table_style + '<table class="clinical-table">'
258
+ html += '<tr>'
259
+ html += '<th>NCTID</th>'
260
+ if show_grade:
261
+ html += '<th>Grade</th>'
262
+ html += '<th>Title</th>'
263
+ if show_grade:
264
+ html += '<th>AI Judgment</th>'
265
+ html += '<th>Japanese Locations</th>'
266
+ html += '<th>Completion Date</th>'
267
+ html += '<th>Cancer Type</th>'
268
+ html += '</tr>'
269
+
270
+ # データ行
271
+ for item in data:
272
+ grade = item.get('AgentGrade', 'unclear')
273
+ grade_class = f"grade-{grade}" if show_grade else ""
274
 
275
+ html += f'<tr class="{grade_class}">'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
 
277
+ # NCTID(リンク付き)
278
+ nctid = item.get('NCTID', '')
279
+ html += f'<td><a href="https://clinicaltrials.gov/ct2/show/{nctid}" target="_blank" class="nctid-link">{nctid}</a></td>'
 
 
 
 
 
 
 
 
280
 
281
+ # Grade
282
+ if show_grade:
283
+ grade_emoji = {'yes': '✅', 'no': '❌', 'unclear': '❓'}.get(grade, '❓')
284
+ html += f'<td style="text-align: center;">{grade_emoji} {grade}</td>'
285
 
286
+ # Title
287
+ title = item.get('Title', '').strip()
288
+ html += f'<td class="title-cell">{title}</td>'
289
 
290
+ # AI Judgment
291
+ if show_grade:
292
+ judgment = item.get('AgentJudgment', '').strip()
293
+ html += f'<td class="criteria-cell">{judgment}</td>'
 
 
 
 
 
 
 
 
 
 
 
 
 
294
 
295
+ # Japanese Locations
296
+ locations = item.get('Japanes Locations', '').strip()
297
+ html += f'<td>{locations}</td>'
298
 
299
+ # Completion Date
300
+ completion_date = item.get('Primary Completion Date', '').strip()
301
+ html += f'<td>{completion_date}</td>'
302
 
303
+ # Cancer Type
304
+ cancer = item.get('Cancer', '').strip()
305
+ html += f'<td>{cancer}</td>'
306
 
307
+ html += '</tr>'
308
+
309
+ html += '</table>'
310
+
311
+ # 統計情報
312
+ if show_grade:
313
+ total = len(data)
314
+ yes_count = len([item for item in data if item.get('AgentGrade') == 'yes'])
315
+ no_count = len([item for item in data if item.get('AgentGrade') == 'no'])
316
+ unclear_count = len([item for item in data if item.get('AgentGrade') == 'unclear'])
317
+
318
+ stats_html = f"""
319
+ <div style='margin: 10px 0; padding: 10px; background-color: #f8f9fa; border-radius: 5px; font-size: 14px;'>
320
+ <strong>📊 統計:</strong>
321
+ 合計 {total} 件 |
322
+ 適格 {yes_count} 件 |
323
+ ❌ 不適格 {no_count} 件 |
324
+ ❓ 要検討 {unclear_count} 件
325
+ </div>
326
+ """
327
+ html = stats_html + html
328
+
329
+ return html
330
 
331
+ # フィルタリング関数
332
+ def filter_data(data, grade):
333
+ """データをフィルタリング"""
334
+ if not data:
335
+ return []
336
+
337
  try:
338
+ if grade == "all":
339
+ return data
340
+ return [item for item in data if item.get('AgentGrade') == grade]
 
 
341
  except Exception as e:
342
+ print(f"フィルタリングエラー: {e}")
343
+ return data
344
 
345
  # システム状態の確認
346
  def get_system_status():
 
354
  else:
355
  return "🔴 軽量版", "サンプルデータのみ表示"
356
 
357
+ # CSV エクスポート関数
358
+ def export_to_csv(data):
359
+ """データをCSVファイルとしてエクスポート"""
360
+ try:
361
+ if not data:
362
+ return None
363
+
364
+ # DataFrame に変換
365
+ df = pd.DataFrame(data)
366
+
367
+ # ファイルパス
368
+ file_path = "clinical_trials_data.csv"
369
+ df.to_csv(file_path, index=False, encoding='utf-8-sig')
370
+
371
+ return file_path
372
+ except Exception as e:
373
+ print(f"CSV エクスポートエラー: {e}")
374
+ return None
375
+
376
  # Gradioインターフェースの作成
377
  with gr.Blocks(title="臨床試験適格性評価", theme=gr.themes.Soft()) as demo:
378
  gr.Markdown("## 🏥 臨床試験適格性評価インターフェース")
 
403
  measurable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Measurable Tumor", value="有り")
404
  biopsiable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Biopsiable Tumor", value="有り")
405
 
406
+ # 結果表示エリア(HTMLテーブル)
407
+ results_html = gr.HTML(label="Clinical Trials Results")
 
 
 
 
408
 
409
+ # 内部状態用
410
+ data_state = gr.State(value=[])
 
411
 
412
  # ボタン類
413
  with gr.Row():
 
425
  all_button = gr.Button("📊 Show All Trials", variant="secondary")
426
 
427
  with gr.Row():
428
+ download_button = gr.Button("💾 Download CSV")
 
429
 
430
  # ダウンロードファイル
431
+ download_output = gr.File(label="Download CSV", visible=False)
 
432
 
433
  # プログレス表示
434
  progress_text = gr.Textbox(label="Processing Status", value="Ready", interactive=False)
435
 
436
  # イベントハンドリング
437
+ def update_data_and_display(age, sex, tumor_type, gene_mutation, measurable, biopsiable):
438
+ """データ生成と表示更新"""
439
  try:
440
  if FULL_VERSION:
441
+ progress_msg = "🔍 実際の臨床試験データを検索中(AI評価付き)..."
442
+ # 完全版の実装はここに追加
443
+ data = generate_sample_data(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
444
  elif LANGCHAIN_AVAILABLE:
445
+ progress_msg = "📡 ClinicalTrials.govから基本データを検索中..."
446
+ data = generate_basic_data(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
447
  else:
448
+ progress_msg = "📋 サンプルデータを生成中..."
449
+ data = generate_sample_data(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
450
 
451
+ if data:
452
+ html_table = create_html_table(data)
453
+ final_progress = f"✅ 完了: {len(data)} 件の臨床試験が見つかりました"
454
  else:
455
+ html_table = "<div style='text-align: center; padding: 20px; color: #666;'>⚠️ 該当する臨床試験が見つかりませんでした</div>"
456
+ final_progress = "⚠️ 該当する臨床試験が見つかりませんでした"
457
 
458
+ return html_table, data, final_progress
459
  except Exception as e:
460
  error_msg = f"❌ エラー: {str(e)}"
461
+ error_html = f"<div style='text-align: center; padding: 20px; color: #d32f2f;'>{error_msg}</div>"
462
+ print(f"データ更新エラー: {e}")
463
+ return error_html, [], error_msg
464
 
465
+ def filter_and_show(data, grade):
466
  """フィルタリングと表示更新"""
 
 
 
467
  try:
468
+ filtered_data = filter_data(data, grade)
469
+ html_table = create_html_table(filtered_data)
470
+ return html_table
 
 
471
  except Exception as e:
472
  print(f"フィルタリングエラー: {e}")
473
+ return create_html_table(data)
474
 
475
+ def download_csv(data):
476
+ """CSV ダウンロード処理"""
477
+ try:
478
+ if not data:
479
+ return None
480
+ return export_to_csv(data)
481
+ except Exception as e:
482
+ print(f"ダウンロードエラー: {e}")
483
+ return None
484
+
485
+ # ボタンイベント
486
  generate_button.click(
487
+ fn=update_data_and_display,
488
  inputs=[age_input, sex_input, tumor_type_input, gene_mutation_input, measurable_input, biopsiable_input],
489
+ outputs=[results_html, data_state, progress_text]
490
  )
491
 
492
  yes_button.click(
493
+ fn=lambda data: filter_and_show(data, "yes"),
494
+ inputs=[data_state],
495
+ outputs=[results_html]
496
  )
497
 
498
  no_button.click(
499
+ fn=lambda data: filter_and_show(data, "no"),
500
+ inputs=[data_state],
501
+ outputs=[results_html]
502
  )
503
 
504
  unclear_button.click(
505
+ fn=lambda data: filter_and_show(data, "unclear"),
506
+ inputs=[data_state],
507
+ outputs=[results_html]
508
  )
509
 
510
  all_button.click(
511
+ fn=lambda data: filter_and_show(data, "all"),
512
+ inputs=[data_state],
513
+ outputs=[results_html]
 
 
 
 
 
 
514
  )
515
 
516
+ download_button.click(
517
+ fn=download_csv,
518
+ inputs=[data_state],
519
+ outputs=[download_output]
520
  )
521
 
522
  # フッター情報
requirements.txt CHANGED
@@ -1,5 +1,5 @@
1
- # Stage 3A: 最小限依存関係(段階的デプロイ用)
2
- gradio==4.36.1
3
  numpy==1.21.6
4
  pandas==1.3.5
5
- requests==2.31.0
 
1
+ # Stage 3A: 安定版(Gradio LTS バージョン)
2
+ gradio==4.20.1
3
  numpy==1.21.6
4
  pandas==1.3.5
5
+ requests==2.31.0