feat(history): enable 'Analysis Report' button in simulation history
- ProjectManager._to_dict now scans simulation state files to find last_simulation_id and last_report_id for each project - HistoryDatabase: report button enabled when last_report_id or last_simulation_id available; goToReport looks up report by simulation ID if no direct report_id; report status icon reflects availability - Add getReportBySimulation() to frontend report API Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f0f8a797ef
commit
e7641e9831
|
|
@ -242,8 +242,43 @@ class ProjectManager:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _to_dict(cls, proj: "ProjectModel") -> Dict[str, Any]:
|
def _to_dict(cls, proj: "ProjectModel") -> Dict[str, Any]:
|
||||||
|
import os, json as _json
|
||||||
ontology = cls.get_ontology(proj.id)
|
ontology = cls.get_ontology(proj.id)
|
||||||
graph_external_id = cls.get_latest_graph_external_id(proj.id)
|
graph_external_id = cls.get_latest_graph_external_id(proj.id)
|
||||||
|
|
||||||
|
# Find the latest simulation for this project by scanning state.json files
|
||||||
|
last_simulation_id = None
|
||||||
|
last_report_id = None
|
||||||
|
sim_base = Config.OASIS_SIMULATION_DATA_DIR
|
||||||
|
if os.path.isdir(sim_base):
|
||||||
|
candidates = []
|
||||||
|
for entry in os.scandir(sim_base):
|
||||||
|
if not entry.is_dir():
|
||||||
|
continue
|
||||||
|
state_path = os.path.join(entry.path, "state.json")
|
||||||
|
if not os.path.exists(state_path):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
with open(state_path, encoding="utf-8") as f:
|
||||||
|
state = _json.load(f)
|
||||||
|
if state.get("project_id") == proj.id:
|
||||||
|
candidates.append((state.get("updated_at", ""), state.get("simulation_id")))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if candidates:
|
||||||
|
candidates.sort(reverse=True)
|
||||||
|
last_simulation_id = candidates[0][1]
|
||||||
|
|
||||||
|
# Find latest report for that simulation
|
||||||
|
if last_simulation_id:
|
||||||
|
from ..services.report_agent import ReportManager
|
||||||
|
try:
|
||||||
|
report = ReportManager.get_report_by_simulation(last_simulation_id)
|
||||||
|
if report:
|
||||||
|
last_report_id = report.report_id
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"id": proj.id,
|
"id": proj.id,
|
||||||
"project_id": proj.id,
|
"project_id": proj.id,
|
||||||
|
|
@ -262,4 +297,6 @@ class ProjectManager:
|
||||||
"graph_id": graph_external_id,
|
"graph_id": graph_external_id,
|
||||||
"graph_build_task_id": None,
|
"graph_build_task_id": None,
|
||||||
"error": None,
|
"error": None,
|
||||||
|
"last_simulation_id": last_simulation_id,
|
||||||
|
"last_report_id": last_report_id,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,3 +50,11 @@ export const chatWithReport = (data) => {
|
||||||
return requestWithRetry(() => service.post('/api/report/chat', data), 3, 1000)
|
return requestWithRetry(() => service.post('/api/report/chat', data), 3, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get report by simulation ID
|
||||||
|
* @param {string} simulationId
|
||||||
|
*/
|
||||||
|
export const getReportBySimulation = (simulationId) => {
|
||||||
|
return service.get(`/api/report/by-simulation/${simulationId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,8 @@
|
||||||
:title="$t('history.envSetup')"
|
:title="$t('history.envSetup')"
|
||||||
>◈</span>
|
>◈</span>
|
||||||
<span
|
<span
|
||||||
class="status-icon unavailable"
|
class="status-icon"
|
||||||
|
:class="{ available: project.last_report_id || project.last_simulation_id, unavailable: !project.last_report_id && !project.last_simulation_id }"
|
||||||
:title="$t('history.analysisReport')"
|
:title="$t('history.analysisReport')"
|
||||||
>◆</span>
|
>◆</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -173,7 +174,7 @@
|
||||||
<button
|
<button
|
||||||
class="modal-btn btn-report"
|
class="modal-btn btn-report"
|
||||||
@click="goToReport"
|
@click="goToReport"
|
||||||
:disabled="true"
|
:disabled="!selectedProject.last_report_id && !selectedProject.last_simulation_id"
|
||||||
>
|
>
|
||||||
<span class="btn-step">Step4</span>
|
<span class="btn-step">Step4</span>
|
||||||
<span class="btn-icon">◆</span>
|
<span class="btn-icon">◆</span>
|
||||||
|
|
@ -196,6 +197,7 @@ import { ref, computed, onMounted, onUnmounted, onActivated, watch, nextTick } f
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { listProjects } from '../api/graph'
|
import { listProjects } from '../api/graph'
|
||||||
|
import { getReportBySimulation } from '../api/report'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
@ -427,8 +429,26 @@ const goToSimulation = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导航到分析报告页面(Report)
|
// 导航到分析报告页面(Report)
|
||||||
const goToReport = () => {
|
const goToReport = async () => {
|
||||||
// Report not yet implemented in F1-1
|
if (!selectedProject.value) return
|
||||||
|
|
||||||
|
// Use known report_id if available
|
||||||
|
let reportId = selectedProject.value.last_report_id
|
||||||
|
|
||||||
|
// Otherwise look it up by simulation_id
|
||||||
|
if (!reportId && selectedProject.value.last_simulation_id) {
|
||||||
|
try {
|
||||||
|
const res = await getReportBySimulation(selectedProject.value.last_simulation_id)
|
||||||
|
reportId = res?.data?.report_id
|
||||||
|
} catch {
|
||||||
|
// no report found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reportId) {
|
||||||
|
closeModal()
|
||||||
|
router.push({ name: 'Interaction', params: { reportId } })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载历史项目
|
// 加载历史项目
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue