fix: add None check for strategy not found in result_view

- Add validation to check if strategy exists before processing
- Return user-friendly error message and redirect to result page
- Prevents TypeError: 'NoneType' object is not iterable
- Ensures Railway deployment has correct result_view.html template
master
Eric0801 2 months ago
parent 4956bdca5f
commit 1ad01649f2
  1. 6
      main.py
  2. 59
      templates/result_view.html

@ -553,6 +553,12 @@ def result_view():
curs.execute(sql, (sid, )) curs.execute(sql, (sid, ))
data= curs.fetchone() data= curs.fetchone()
conn.close() conn.close()
# Check if strategy exists
if not data:
flash(f'策略 ID {sid} 不存在', 'warning')
return redirect(url_for('result'))
# Processing data # Processing data
data = dict(data) data = dict(data)
data['role'] = role_map.get(data['role'], data['role']) data['role'] = role_map.get(data['role'], data['role'])

@ -260,7 +260,7 @@
<th scope="col">Beta</th> <th scope="col">Beta</th>
<th scope="col">VaR10</th> <th scope="col">VaR10</th>
<th scope="col">R2</th> <th scope="col">R2</th>
{% if data.role == '最大化效函數' %} {% if data.role == '最大化效<EFBFBD><EFBFBD><EFBFBD>函數' %}
<th scope="col">Gamma</th> <th scope="col">Gamma</th>
{% endif %} {% endif %}
</tr> </tr>
@ -302,7 +302,7 @@
</div> </div>
<div class="mb-4" id="bar" style="max-height:60vh"></div> <div class="mb-4" id="bar" style="max-height:60vh"></div>
<div class="row justify-content-center font-bold text-xl"> <div class="row justify-content-center font-bold text-xl">
🤖 AI 投資建議 🤖 LLM 投組分析
</div> </div>
<div class="card mb-4" style="border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);"> <div class="card mb-4" style="border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
<div class="card-body"> <div class="card-body">
@ -311,35 +311,15 @@
<div class="spinner-border spinner-border-sm" role="status"> <div class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden">Loading...</span> <span class="visually-hidden">Loading...</span>
</div> </div>
<p class="mt-2">正在生成AI投資建議...</p> <p class="mt-2">正在生成 LLM 投組分析...</p>
</div> </div>
</div> </div>
<!-- 重新生成按鈕組 --> <!-- 重新生成按鈕組 -->
<div class="d-flex gap-2 mt-3 align-items-center flex-wrap"> <div class="d-flex gap-2 mt-3 align-items-center flex-wrap">
<button id="refresh-advice" class="btn btn-outline-primary btn-sm" onclick="refreshLLMAdvice()"> <button id="refresh-advice" class="btn btn-outline-primary btn-sm" onclick="refreshLLMAdvice()">
🔄 重新生成建議 🔄 重新生成分析
</button> </button>
<!-- CoT 開關 -->
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="cot-toggle" checked>
<label class="form-check-label" for="cot-toggle">
🧠 思考鏈推理 (CoT)
</label>
</div>
{% comment %} <!-- Popover 選項按鈕 -->
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-toggle="popover"
data-bs-placement="top" data-bs-html="true"
data-bs-content="<div class='mb-2'><strong>選擇分析深度:</strong></div>
<div class='d-grid gap-1'>
<button class='btn btn-sm btn-outline-info' onclick='generateAdviceWithMode(\"simple\")'>📊 快速分析</button>
<button class='btn btn-sm btn-outline-warning' onclick='generateAdviceWithMode(\"comprehensive\")'>🔍 深度分析</button>
<button class='btn btn-sm btn-outline-danger' onclick='generateAdviceWithMode(\"risk\")'> 風險分析</button>
</div>">
分析選項
</button> {% endcomment %}
</div> </div>
</div> </div>
</div> </div>
@ -426,46 +406,35 @@
async function refreshLLMAdvice() { async function refreshLLMAdvice() {
const container = document.getElementById('llm-advice-container'); const container = document.getElementById('llm-advice-container');
const button = document.getElementById('refresh-advice'); const button = document.getElementById('refresh-advice');
const cotToggle = document.getElementById('cot-toggle');
try { try {
// 禁用按鈕 // 禁用按鈕
button.disabled = true; button.disabled = true;
button.innerHTML = '<span class="spinner-border spinner-border-sm"></span> 正在生成...'; button.innerHTML = '<span class="spinner-border spinner-border-sm"></span> 正在生成...';
// 獲取CoT開關狀態
const useCot = cotToggle.checked;
const cotLabel = useCot ? '(使用思考鏈推理)' : '';
// 顯示loading狀態 // 顯示loading狀態
container.innerHTML = ` container.innerHTML = `
<div class="text-center text-muted"> <div class="text-center text-muted">
<div class="spinner-border spinner-border-sm" role="status"> <div class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden">Loading...</span> <span class="visually-hidden">Loading...</span>
</div> </div>
<p class="mt-2">正在生成AI投資建議${cotLabel}...</p> <p class="mt-2">正在生成 LLM 投組分析...</p>
</div> </div>
`; `;
// 調用API,加上cot參數 // 調用API,使用 CoT(更詳細的思考過程)
const url = `/api/llm_advice/${strategyId}${useCot ? '?cot=true' : ''}`; const url = `/api/llm_advice/${strategyId}`;
const response = await fetch(url); const response = await fetch(url);
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
// 格式化並顯示建議 // 格式化並顯示建議
const formattedAdvice = formatLLMAdvice(result.advice); const formattedAdvice = formatLLMAdvice(result.advice);
container.innerHTML = formattedAdvice;
// 如果啟用CoT,顯示標記
const cotBadge = result.cot_enabled
? '<span class="badge bg-info mb-2">🧠 Chain-of-Thought 推理模式</span><br>'
: '';
container.innerHTML = cotBadge + formattedAdvice;
} else { } else {
container.innerHTML = ` container.innerHTML = `
<div class="alert alert-warning"> <div class="alert alert-warning">
<h6> 無法生成投資建議</h6> <h6> 無法生成 LLM 投組分析</h6>
<p>${result.error}</p> <p>${result.error}</p>
${result.details ? `<small class="text-muted">${result.details}</small>` : ''} ${result.details ? `<small class="text-muted">${result.details}</small>` : ''}
</div> </div>
@ -475,14 +444,14 @@
container.innerHTML = ` container.innerHTML = `
<div class="alert alert-danger"> <div class="alert alert-danger">
<h6>❌ 發生錯誤</h6> <h6>❌ 發生錯誤</h6>
<p>無法連接到投資建議服務,請稍後再試。</p> <p>無法連接到 LLM 投組分析服務,請稍後再試。</p>
<small class="text-muted">錯誤詳情:${error.message}</small> <small class="text-muted">錯誤詳情:${error.message}</small>
</div> </div>
`; `;
} finally { } finally {
// 恢復按鈕 // 恢復按鈕
button.disabled = false; button.disabled = false;
button.innerHTML = '🔄 重新生成建議'; button.innerHTML = '🔄 重新生成分析';
} }
} }
@ -619,7 +588,7 @@
} else { } else {
container.innerHTML = ` container.innerHTML = `
<div class="alert alert-warning"> <div class="alert alert-warning">
<h6> 無法生成投資建議</h6> <h6> 無法生成 LLM 投組分析</h6>
<p>${result.error}</p> <p>${result.error}</p>
${result.details ? `<small class="text-muted">${result.details}</small>` : ''} ${result.details ? `<small class="text-muted">${result.details}</small>` : ''}
</div> </div>
@ -629,7 +598,7 @@
container.innerHTML = ` container.innerHTML = `
<div class="alert alert-danger"> <div class="alert alert-danger">
<h6>❌ 發生錯誤</h6> <h6>❌ 發生錯誤</h6>
<p>無法連接到投資建議服務,請稍後再試。</p> <p>無法連接到 LLM 投組分析服務,請稍後再試。</p>
<small class="text-muted">錯誤詳情:${error.message}</small> <small class="text-muted">錯誤詳情:${error.message}</small>
</div> </div>
`; `;
@ -640,7 +609,7 @@
} }
} }
// 獲取模描述 // 獲取模<EFBFBD><EFBFBD>描述
function getModeDescription(mode) { function getModeDescription(mode) {
const descriptions = { const descriptions = {
'simple': '快速分析', 'simple': '快速分析',

Loading…
Cancel
Save