feat(asl): Complete Tool 4 SR Chart Generator and Tool 5 Meta Analysis Engine
Tool 4 - SR Chart Generator: - PRISMA 2020 flow diagram with Chinese/English toggle (SVG) - Baseline characteristics table (Table 1) - Dual data source: project pipeline API + Excel upload - SVG/PNG export support - Backend: ChartingService with Prisma aggregation - Frontend: PrismaFlowDiagram, BaselineTable, DataSourceSelector Tool 5 - Meta Analysis Engine: - 3 data types: HR (metagen), dichotomous (metabin), continuous (metacont) - Random and fixed effects models - Multiple effect measures: HR / OR / RR - Forest plot + funnel plot (base64 PNG from R) - Heterogeneity statistics: I2, Q, p-value, Tau2 - Data input via Excel upload or project pipeline - R Docker image updated with meta package (13 tools total) - E2E test: 36/36 passed - Key fix: exp() back-transformation for log-scale ratio measures Also includes: - IIT CRA Agent V3.0 routing and AI chat page integration - Updated ASL module status guide (v2.3) - Updated system status guide (v6.3) - Updated R statistics engine guide (v1.4) Tested: Frontend renders correctly, backend APIs functional, E2E tests passed Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Table, Typography, Empty } from 'antd';
|
||||
import type { BaselineRow } from '../../utils/chartingExcelUtils';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
interface Props {
|
||||
data: BaselineRow[];
|
||||
}
|
||||
|
||||
const KNOWN_FIELD_LABELS: Record<string, string> = {
|
||||
Study_ID: 'Study ID',
|
||||
study_id: 'Study ID',
|
||||
Intervention_Name: 'Intervention',
|
||||
intervention_name: 'Intervention',
|
||||
Control_Name: 'Control',
|
||||
control_name: 'Control',
|
||||
Intervention_N: 'Intervention N',
|
||||
intervention_n: 'Intervention N',
|
||||
Control_N: 'Control N',
|
||||
control_n: 'Control N',
|
||||
Age_Mean_SD: 'Age (Mean ± SD)',
|
||||
age_mean_sd: 'Age (Mean ± SD)',
|
||||
Male_Percent: 'Male %',
|
||||
male_percent: 'Male %',
|
||||
study_design: 'Study Design',
|
||||
Study_Design: 'Study Design',
|
||||
};
|
||||
|
||||
function humanize(key: string): string {
|
||||
if (KNOWN_FIELD_LABELS[key]) return KNOWN_FIELD_LABELS[key];
|
||||
return key
|
||||
.replace(/_/g, ' ')
|
||||
.replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
}
|
||||
|
||||
const BaselineTable: React.FC<Props> = ({ data }) => {
|
||||
const columns = useMemo(() => {
|
||||
if (data.length === 0) return [];
|
||||
|
||||
const allKeys = new Set<string>();
|
||||
data.forEach((row) => {
|
||||
Object.keys(row).forEach((k) => allKeys.add(k));
|
||||
});
|
||||
|
||||
const priorityOrder = [
|
||||
'Study_ID', 'study_id',
|
||||
'Intervention_Name', 'intervention_name',
|
||||
'Control_Name', 'control_name',
|
||||
'Intervention_N', 'intervention_n',
|
||||
'Control_N', 'control_n',
|
||||
'Age_Mean_SD', 'age_mean_sd',
|
||||
'Male_Percent', 'male_percent',
|
||||
];
|
||||
|
||||
const orderedKeys: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
for (const pk of priorityOrder) {
|
||||
if (allKeys.has(pk) && !seen.has(pk)) {
|
||||
orderedKeys.push(pk);
|
||||
seen.add(pk);
|
||||
}
|
||||
}
|
||||
for (const k of allKeys) {
|
||||
if (!seen.has(k)) {
|
||||
orderedKeys.push(k);
|
||||
seen.add(k);
|
||||
}
|
||||
}
|
||||
|
||||
return orderedKeys.map((key) => ({
|
||||
title: humanize(key),
|
||||
dataIndex: key,
|
||||
key,
|
||||
ellipsis: true,
|
||||
render: (val: any) => {
|
||||
if (val === null || val === undefined) return '-';
|
||||
if (typeof val === 'object') return JSON.stringify(val);
|
||||
return String(val);
|
||||
},
|
||||
}));
|
||||
}, [data]);
|
||||
|
||||
if (data.length === 0) {
|
||||
return <Empty description="暂无基线数据" />;
|
||||
}
|
||||
|
||||
const dataSource = data.map((row, i) => ({ ...row, _rowKey: `row-${i}` }));
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<Title level={5} className="text-center mb-4">
|
||||
Table 1. 纳入研究的基线特征 (Baseline Characteristics of Included Studies)
|
||||
</Title>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
rowKey="_rowKey"
|
||||
pagination={false}
|
||||
bordered
|
||||
size="small"
|
||||
scroll={{ x: 'max-content' }}
|
||||
className="baseline-academic-table"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BaselineTable;
|
||||
Reference in New Issue
Block a user