stockany2 / app /web /templates /agent_analysis.html
fiewolf1000's picture
Upload 67 files
0f10134 verified
{% extends "layout.html" %}
{% block head %}
<style>
/* 修复“开始分析”按钮中加载动画和文字的对齐问题 */
#start-analysis-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem; /* 在图标/加载动画和文字之间添加一些间距 */
}
/* 为市场下拉菜单应用自定义样式 */
#market-type.form-select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 0.75rem center;
background-size: 16px 12px;
}
</style>
{% endblock %}
{% block title %}智能体分析 - 智能分析系统{% endblock %}
{% block content %}
<div class="container-fluid py-3">
<div id="alerts-container"></div>
<!-- Page Title -->
<div class="row mb-3">
<div class="col-12">
<h2 class="mb-0">智能体分析</h2>
<p class="text-muted">利用多智能体框架进行深度股票评估</p>
</div>
</div>
<!-- Input Form Card -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header py-2">
<h5 class="mb-0">分析设置</h5>
</div>
<div class="card-body py-2">
<form id="agent-analysis-form" class="row g-3 align-items-end">
<div class="col-md-3">
<label for="stock-code" class="form-label">股票代码</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-barcode"></i></span>
<input type="text" class="form-control" id="stock-code" placeholder="例如: 600519, AAPL" required>
</div>
</div>
<div class="col-md-2">
<label for="market-type" class="form-label">市场</label>
<div class="input-group">
<select class="form-select" id="market-type">
<option value="A" selected>A股</option>
<option value="US">美股</option>
<option value="HK">港股</option>
</select>
</div>
</div>
<div class="col-md-2">
<label for="analysis-date" class="form-label">分析日期</label>
<div class="input-group">
<input type="date" class="form-control" id="analysis-date">
</div>
</div>
<div class="col-md-3">
<label for="research-depth" class="form-label">研究深度</label>
<select class="form-select" id="research-depth">
<option value="1">1级 - 快速</option>
<option value="2">2级 - 基础</option>
<option value="3" selected>3级 - 标准</option>
<option value="4">4级 - 深度</option>
<option value="5">5级 - 全面</option>
</select>
</div>
<div class="col-md-2">
<button type="submit" id="start-analysis-btn" class="btn btn-primary w-100">
<i class="fas fa-rocket"></i> 开始分析
</button>
</div>
<div class="col-12 mt-2">
<label class="form-label mb-1">选择分析师团队:</label>
<div id="analyst-selection" class="d-flex flex-wrap gap-3">
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="analyst-market" value="market" checked>
<label class="form-check-label" for="analyst-market">市场</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="analyst-news" value="news" checked>
<label class="form-check-label" for="analyst-news">新闻</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="analyst-social" value="social" checked>
<label class="form-check-label" for="analyst-social">社交</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="analyst-fundamentals" value="fundamentals" checked>
<label class="form-check-label" for="analyst-fundamentals">基本面</label>
</div>
</div>
</div>
<div class="col-md-9 mt-2">
<label for="max-output-length" class="form-label">最大输出长度: <span id="max-output-length-value" class="fw-bold text-primary">2048</span> tokens</label>
<input type="range" class="form-range" id="max-output-length" min="512" max="8192" step="256" value="2048">
</div>
<div class="col-md-3 mt-2 d-flex align-items-end justify-content-end">
<div class="form-check form-switch pb-2">
<input class="form-check-input" type="checkbox" id="enable-memory" checked>
<label class="form-check-label" for="enable-memory">启用记忆功能</label>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Results Area -->
<div id="results-area" class="mt-4" style="display: none;">
<!-- Progress Bar -->
<div id="progress-container" class="mb-3">
<h5 id="progress-step" class="text-center mb-2">正在初始化...</h5>
<div class="progress" style="height: 20px;">
<div id="progress-bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
</div>
</div>
<!-- Analysis Results Card -->
<div id="results-card" class="card" style="display: none;">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">分析报告</h5>
<div id="export-buttons">
<!-- Export buttons will be added here -->
</div>
</div>
<div class="card-body">
<div id="results-content">
<!-- Dynamic results will be rendered here -->
</div>
</div>
</div>
</div>
<!-- History Card -->
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center py-2">
<h5 class="mb-0"><i class="fas fa-history me-2"></i>分析历史</h5>
<button id="refresh-history-btn" class="btn btn-sm btn-outline-primary">
<i class="fas fa-sync-alt"></i> 刷新
</button>
</div>
<div class="card-body">
<div id="history-table-container">
<!-- History table will be rendered here -->
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
$(document).ready(function() {
let pollingInterval;
$('#agent-analysis-form').submit(function(e) {
e.preventDefault();
const stockCode = $('#stock-code').val().trim();
const researchDepth = $('#research-depth').val();
const marketType = $('#market-type').val();
const analysisDate = $('#analysis-date').val();
const enableMemory = $('#enable-memory').is(':checked');
const maxOutputLength = $('#max-output-length').val();
const selectedAnalysts = $('#analyst-selection input:checked').map(function() {
return $(this).val();
}).get();
if (!stockCode) {
showError('请输入股票代码!');
return;
}
if (selectedAnalysts.length === 0) {
showError('请至少选择一个分析师!');
return;
}
// Disable button and show progress
$('#start-analysis-btn').prop('disabled', true).html('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> 分析中...');
$('#results-area').show();
$('#progress-container').show();
$('#results-card').hide();
updateProgress(0, '任务已提交...');
// Start analysis
$.ajax({
url: '/api/start_agent_analysis',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
stock_code: stockCode,
market_type: marketType,
selected_analysts: selectedAnalysts,
research_depth: parseInt(researchDepth),
analysis_date: analysisDate,
enable_memory: enableMemory,
max_output_length: parseInt(maxOutputLength)
}),
success: function(response) {
if (response.task_id) {
startPolling(response.task_id);
} else {
showError(response.error || '无法启动分析任务');
resetForm();
}
},
error: function(xhr) {
const errorMsg = xhr.responseJSON ? xhr.responseJSON.error : '请求失败';
showError('启动分析失败: ' + errorMsg);
resetForm();
}
});
});
function startPolling(taskId) {
pollingInterval = setInterval(function() {
$.ajax({
url: `/api/agent_analysis_status/${taskId}`,
type: 'GET',
cache: false, // 禁用缓存,确保每次都获取最新状态
success: function(response) {
if (!response) return;
// 优先使用 result.current_step,如果没有则使用默认文本
const currentStep = response.result ? response.result.current_step : '处理中...';
updateProgress(response.progress, currentStep);
if (response.status === 'completed' || response.status === 'failed') {
clearInterval(pollingInterval);
if (response.status === 'completed') {
displayResults(response.result);
} else {
showError(response.error || '分析任务失败');
}
resetForm();
}
},
error: function(xhr) {
clearInterval(pollingInterval);
const errorMsg = xhr.responseJSON ? xhr.responseJSON.error : '请求失败';
showError('获取状态失败: ' + errorMsg);
resetForm();
}
});
}, 3000); // Poll every 3 seconds
}
function updateProgress(percent, step) {
$('#alerts-container').empty(); // 清除“正在恢复”等提示信息
percent = Math.min(percent, 100);
$('#progress-bar').css('width', percent + '%').attr('aria-valuenow', percent).text(percent + '%');
$('#progress-step').text(step);
}
function displayResults(result) {
$('#progress-container').hide();
$('#results-card').show();
let contentHtml = '<h4><i class="fas fa-exclamation-triangle text-danger me-2"></i>分析结果加载失败</h4>';
if (result && result.decision) {
const decision = result.decision;
const state = result.final_state || {};
const stockInfo = {
code: state.company_of_interest || 'N/A',
name: state.company_name || '未知公司',
market: state.market_type || 'N/A'
};
// --- Helper functions ---
const parseFundamentals = (report) => {
if (typeof report !== 'string') {
return {};
}
const metrics = {};
const patterns = {
debt_ratio: /\*\*?资产负债率\*\*?\s*[::\s是为]*\s*([\d\.]+)/i,
liquidity_ratio: /\*\*?流动比率\*\*?\s*[::\s是为]*\s*([\d\.]+)/i,
quick_ratio: /\*\*?速动比率\*\*?\s*[::\s是为]*\s*([\d\.]+)/i,
gross_margin: /\*\*?毛利率\*\*?\s*[::\s是为]*\s*([\d\.]+)/i,
net_margin: /\*\*?净利率\*\*?\s*[::\s是为]*\s*([\d\.]+)/i,
pb_ratio: /\*\*?市净率(?:\(PB\))?\*\*?\s*[::\s是为]*\s*([\d\.]+)/i
};
for (const key in patterns) {
const match = report.match(patterns[key]);
if (match && match[1]) {
metrics[key] = parseFloat(match[1]);
} else {
metrics[key] = null;
}
}
return metrics;
};
const fundamentals = parseFundamentals(state.fundamentals_report);
const getRatingClass = (value, thresholds, ascending = true) => {
if (value === null || value === undefined || isNaN(value)) return 'text-muted';
const [low, mid, high] = thresholds;
if (ascending) {
if (value >= high) return 'text-success';
if (value >= mid) return 'text-primary';
if (value >= low) return 'text-warning';
return 'text-danger';
} else {
if (value <= low) return 'text-success';
if (value <= mid) return 'text-primary';
if (value <= high) return 'text-warning';
return 'text-danger';
}
};
const getRatingText = (value, thresholds, labels, ascending = true) => {
if (value === null || value === undefined || isNaN(value)) return 'N/A';
const [low, mid, high] = thresholds;
const [labelLow, labelMid, labelHigh, labelVeryHigh] = labels;
if (ascending) {
if (value >= high) return labelVeryHigh;
if (value >= mid) return labelHigh;
if (value >= low) return labelMid;
return labelLow;
} else {
if (value <= low) return labelVeryHigh;
if (value <= mid) return labelHigh;
if (value <= high) return labelMid;
return labelLow;
}
};
const renderTable = (title, headers, rows) => {
let tableHtml = `<div class="card h-100 shadow-sm">
<div class="card-header bg-light">
<h6 class="mb-0">${title}</h6>
</div>
<div class="card-body p-0">
<table class="table table-sm table-hover mb-0">
<thead><tr>${headers.map(h => `<th>${h}</th>`).join('')}</tr></thead>
<tbody>`;
rows.forEach(row => {
tableHtml += `<tr>${row.map(cell => `<td>${cell}</td>`).join('')}</tr>`;
});
tableHtml += `</tbody></table></div></div>`;
return tableHtml;
};
// --- Main Content ---
contentHtml = `
<!-- Header -->
<div class="text-center mb-4">
<h2>${stockInfo.name} (${stockInfo.code}) - 智能体深度分析报告</h2>
<p class="text-muted">报告生成日期: ${new Date().toLocaleDateString()}</p>
</div>
<!-- Investment Suggestion -->
<div class="alert ${getDecisionClass(decision.action).replace('text-', 'alert-')} border-0 shadow-lg mb-4">
<h4 class="alert-heading"><i class="fas fa-lightbulb me-2"></i>投资建议: <span class="fw-bold">${decision.action}</span></h4>
<p class="mb-0">${decision.reasoning}</p>
</div>
<!-- Key Metrics -->
<div class="row mb-4">
<div class="col-md-4">
<div class="card text-center h-100 shadow-sm">
<div class="card-body">
<h6 class="text-muted">置信度</h6>
<p class="display-5 fw-bold mb-0">${(decision.confidence * 100).toFixed(1)}%</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card text-center h-100 shadow-sm">
<div class="card-body">
<h6 class="text-muted">风险评分</h6>
<p class="display-5 fw-bold mb-0 ${getRatingClass(decision.risk_score * 100, [30, 50, 70], false)}">${(decision.risk_score * 100).toFixed(1)}%</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card text-center h-100 shadow-sm">
<div class="card-body">
<h6 class="text-muted">市场情绪</h6>
<p class="display-5 fw-bold mb-0">${state.sentiment_score || 'N/A'}</p>
</div>
</div>
</div>
</div>
<!-- Financial, Profitability & Valuation Tables -->
<div class="row g-4 mb-4">
<div class="col-lg-6">
${renderTable(
'<i class="fas fa-balance-scale me-2"></i>财务状况评估',
['指标', '数值', '评估'],
[
['资产负债率', fundamentals.debt_ratio !== null ? `${fundamentals.debt_ratio}%` : 'N/A', `<span class="${getRatingClass(fundamentals.debt_ratio, [40, 60, 80], false)}">${getRatingText(fundamentals.debt_ratio, [40, 60, 80], ['高风险', '较高风险', '中等', '良好'], false)}</span>`],
['流动比率', fundamentals.liquidity_ratio !== null ? fundamentals.liquidity_ratio : 'N/A', `<span class="${getRatingClass(fundamentals.liquidity_ratio, [1, 1.5, 2], true)}">${getRatingText(fundamentals.liquidity_ratio, [1, 1.5, 2], ['风险', '吃紧', '良好', '优秀'], true)}</span>`],
['速动比率', fundamentals.quick_ratio !== null ? fundamentals.quick_ratio : 'N/A', `<span class="${getRatingClass(fundamentals.quick_ratio, [0.8, 1, 1.2], true)}">${getRatingText(fundamentals.quick_ratio, [0.8, 1, 1.2], ['危险', '风险', '尚可', '良好'], true)}</span>`]
]
)}
</div>
<div class="col-lg-6">
${renderTable(
'<i class="fas fa-chart-line me-2"></i>盈利与估值',
['指标', '数值', '评估'],
[
['毛利率', fundamentals.gross_margin !== null ? `${fundamentals.gross_margin}%` : 'N/A', `<span class="${getRatingClass(fundamentals.gross_margin, [10, 20, 30], true)}">${getRatingText(fundamentals.gross_margin, [10, 20, 30], ['极低', '偏低', '中等', '优秀'], true)}</span>`],
['净利率', fundamentals.net_margin !== null ? `${fundamentals.net_margin}%` : 'N/A', `<span class="${getRatingClass(fundamentals.net_margin, [0, 5, 10], true)}">${getRatingText(fundamentals.net_margin, [0, 5, 10], ['亏损', '微利', '良好', '优秀'], true)}</span>`],
['市净率(PB)', fundamentals.pb_ratio !== null ? fundamentals.pb_ratio : 'N/A', `<span class="${getRatingClass(fundamentals.pb_ratio, [1, 2, 4], false)}">${getRatingText(fundamentals.pb_ratio, [1, 2, 4], ['高估', '偏高', '合理', '低估'], false)}</span>`]
]
)}
</div>
</div>
<!-- Detailed Reports Accordion -->
<h3 class="mt-5 mb-3">详细分析报告</h3>
<div class="accordion" id="reports-accordion">
`;
const reports = [
{ title: '<i class="fas fa-chart-pie me-2"></i>市场分析报告', content: state.market_report, id: 'market' },
{ title: '<i class="fas fa-comments me-2"></i>社交媒体情绪报告', content: state.sentiment_report, id: 'social' },
{ title: '<i class="far fa-newspaper me-2"></i>新闻分析报告', content: state.news_report, id: 'news' },
{ title: '<i class="fas fa-file-invoice-dollar me-2"></i>基本面分析报告', content: state.fundamentals_report, id: 'fundamentals' },
{ title: '<i class="fas fa-gavel me-2"></i>投资辩论总结', content: state.investment_debate_state?.judge_decision, id: 'invest_debate' },
{ title: '<i class="fas fa-exclamation-triangle me-2"></i>风险分析总结', content: state.risk_debate_state?.judge_decision, id: 'risk_debate' }
];
reports.forEach((report, index) => {
if (report.content && (typeof report.content === 'string' ? report.content.trim() !== '' : true)) {
let reportContent;
if (typeof report.content === 'object') {
reportContent = `<pre>${JSON.stringify(report.content, null, 2)}</pre>`;
} else if (typeof report.content === 'string') {
// 使用Marked.js渲染Markdown
reportContent = marked.parse(report.content);
} else {
reportContent = '<p>内容格式无法解析。</p>';
}
contentHtml += `
<div class="accordion-item">
<h2 class="accordion-header" id="heading-${report.id}">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-${report.id}" aria-expanded="false" aria-controls="collapse-${report.id}">
<strong>${report.title}</strong>
</button>
</h2>
<div id="collapse-${report.id}" class="accordion-collapse collapse" aria-labelledby="heading-${report.id}" data-bs-parent="#reports-accordion">
<div class="accordion-body bg-light">
${reportContent}
</div>
</div>
</div>
`;
}
});
contentHtml += '</div>';
}
$('#results-content').html(contentHtml);
}
function getDecisionClass(action) {
if (action.toLowerCase().includes('buy') || action.toLowerCase().includes('买入')) {
return 'text-success';
} else if (action.toLowerCase().includes('sell') || action.toLowerCase().includes('卖出')) {
return 'text-danger';
}
return 'text-warning';
}
function resetForm() {
$('#start-analysis-btn').prop('disabled', false).html('<i class="fas fa-rocket"></i> 开始分析');
}
// You can reuse the showError function from layout.html if it's globally available
function showError(message) {
const alertHtml = `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
$('#alerts-container').html(alertHtml);
}
// Check for task_id in URL on page load to resume polling
const urlParams = new URLSearchParams(window.location.search);
const taskIdFromUrl = urlParams.get('task_id');
if (taskIdFromUrl) {
showInfo('正在恢复分析任务...');
$('#results-area').show();
$('#progress-container').show();
$('#results-card').hide();
// Fetch initial task state to get stock code
$.ajax({
url: `/api/agent_analysis_status/${taskIdFromUrl}`,
type: 'GET',
success: function(task) {
if (task && task.params && task.params.stock_code) {
$('#stock-code').val(task.params.stock_code);
}
startPolling(taskIdFromUrl);
},
error: function() {
showError('无法恢复分析任务,任务ID可能已失效。');
}
});
}
// --- History Section Logic ---
function loadHistory() {
$('#history-table-container').html('<div class="text-center p-3"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div></div>');
$.ajax({
url: '/api/agent_analysis_history',
type: 'GET',
cache: false,
success: function(response) {
if (response && response.history) {
renderHistoryTable(response.history);
} else {
showError('无法加载分析历史。');
}
},
error: function(xhr) {
const errorMsg = xhr.responseJSON ? xhr.responseJSON.error : '请求失败';
showError('加载历史失败: ' + errorMsg);
$('#history-table-container').html('<div class="alert alert-danger">加载历史记录失败。</div>');
}
});
}
function renderHistoryTable(history) {
if (history.length === 0) {
$('#history-table-container').html('<div class="alert alert-info">没有找到任何分析历史记录。</div>');
return;
}
let tableHtml = `
<div class="mb-3">
<button id="delete-selected-btn" class="btn btn-danger" disabled>
<i class="fas fa-trash-alt me-2"></i>删除选中项
</button>
</div>
<div class="table-responsive">
<table class="table table-hover table-sm align-middle">
<thead>
<tr>
<th><input class="form-check-input" type="checkbox" id="select-all-checkbox"></th>
<th>#</th>
<th>股票代码</th>
<th>状态</th>
<th>研究深度</th>
<th>分析师</th>
<th>创建时间</th>
<th>更新时间</th>
<th class="text-end">操作</th>
</tr>
</thead>
<tbody>
`;
history.forEach((task, index) => {
const params = task.params || {};
const analysts = params.selected_analysts || [];
const statusBadge = getStatusBadge(task.status);
tableHtml += `
<tr data-task-id="${task.id}">
<td><input class="form-check-input row-checkbox" type="checkbox" value="${task.id}"></td>
<td>${index + 1}</td>
<td><strong>${params.stock_code || 'N/A'}</strong> <span class="badge bg-secondary">${params.market_type || ''}</span></td>
<td>${statusBadge}</td>
<td><span class="badge bg-info">${params.research_depth || 'N/A'}级</span></td>
<td><small>${analysts.join(', ')}</small></td>
<td><small>${task.created_at}</small></td>
<td><small>${task.updated_at}</small></td>
<td class="text-end">
<a href="/agent_analysis?task_id=${task.id}" class="btn btn-sm btn-primary">
<i class="fas fa-eye"></i> 查看
</a>
<button class="btn btn-sm btn-outline-danger delete-single-btn" data-task-id="${task.id}">
<i class="fas fa-trash-alt"></i>
</button>
</td>
</tr>
`;
});
tableHtml += '</tbody></table></div>';
$('#history-table-container').html(tableHtml);
}
function getStatusBadge(status) {
if (status === 'completed') {
return '<span class="badge bg-success">已完成</span>';
} else if (status === 'failed') {
return '<span class="badge bg-danger">失败</span>';
}
return `<span class="badge bg-secondary">${status}</span>`;
}
$('#refresh-history-btn').click(function() {
loadHistory();
});
// Initial load of history
loadHistory();
// --- Deletion and Selection Logic ---
function performDeletion(taskIds) {
if (!confirm(`您确定要删除 ${taskIds.length} 个分析记录吗?此操作无法撤销。`)) {
return;
}
$.ajax({
url: '/api/delete_agent_analysis',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ task_ids: taskIds }),
success: function(response) {
if (response.success) {
showSuccess(response.message || '成功删除记录');
loadHistory(); // Refresh the history table
} else {
showError(response.error || '删除失败');
}
},
error: function(xhr) {
const errorMsg = xhr.responseJSON ? xhr.responseJSON.error : '请求失败';
showError('删除操作失败: ' + errorMsg);
}
});
}
// Event delegation for dynamically added elements
$('#history-table-container').on('click', '.delete-single-btn', function() {
const taskId = $(this).data('task-id');
performDeletion([taskId]);
});
$('#history-table-container').on('click', '#delete-selected-btn', function() {
const selectedIds = $('.row-checkbox:checked').map(function() {
return $(this).val();
}).get();
if (selectedIds.length > 0) {
performDeletion(selectedIds);
}
});
// Checkbox selection logic
$('#history-table-container').on('change', '#select-all-checkbox', function() {
$('.row-checkbox').prop('checked', $(this).prop('checked'));
updateDeleteButtonState();
});
$('#history-table-container').on('change', '.row-checkbox', function() {
if ($('.row-checkbox:checked').length === $('.row-checkbox').length) {
$('#select-all-checkbox').prop('checked', true);
} else {
$('#select-all-checkbox').prop('checked', false);
}
updateDeleteButtonState();
});
// Set analysis date to today by default
document.getElementById('analysis-date').valueAsDate = new Date();
// Handle max output length slider
const maxLengthSlider = document.getElementById('max-output-length');
const maxLengthValue = document.getElementById('max-output-length-value');
if (maxLengthSlider) {
maxLengthSlider.addEventListener('input', function() {
maxLengthValue.textContent = this.value;
});
}
});
</script>
{% endblock %}