Files
AIclinicalresearch/backend/test-form-status.ts

165 lines
6.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 测试脚本:验证表单状态和计算字段获取
*/
import { PrismaClient } from '@prisma/client';
import { RedcapAdapter } from './src/modules/iit-manager/adapters/RedcapAdapter.js';
const prisma = new PrismaClient();
async function main() {
console.log('='.repeat(60));
console.log('📋 表单状态和计算字段测试');
console.log('='.repeat(60));
// 1. 获取项目配置
const project = await prisma.iitProject.findFirst({
where: { name: { contains: 'test0207' } },
});
if (!project) {
console.log('❌ 未找到项目');
await prisma.$disconnect();
return;
}
const adapter = new RedcapAdapter(project.redcapUrl, project.redcapApiToken);
// ===============================
// 测试 1获取计算字段列表
// ===============================
console.log('\n' + '='.repeat(60));
console.log('📊 测试 1计算字段列表');
console.log('='.repeat(60));
const calcFields = await adapter.getCalculatedFields();
console.log(`\n共有 ${calcFields.length} 个计算字段:`);
for (const field of calcFields) {
console.log(` 📐 ${field.fieldName} (${field.fieldLabel})`);
console.log(` 表单: ${field.formName}`);
console.log(` 公式: ${field.calculation.substring(0, 80)}${field.calculation.length > 80 ? '...' : ''}`);
}
// ===============================
// 测试 2获取表单完成状态
// ===============================
console.log('\n' + '='.repeat(60));
console.log('📊 测试 2表单完成状态 (前 5 条记录)');
console.log('='.repeat(60));
const formStatus = await adapter.getFormCompletionStatus();
// 只显示前 5 条
for (const record of formStatus.slice(0, 5)) {
console.log(`\n [Record ${record.recordId}] ${record.allComplete ? '✅ 全部完成' : '⚠️ 部分未完成'}`);
// 统计各状态数量
const statusCounts = { Complete: 0, Unverified: 0, Incomplete: 0 };
for (const [formName, status] of Object.entries(record.forms)) {
statusCounts[status.statusLabel]++;
}
console.log(` Complete: ${statusCounts.Complete}, Unverified: ${statusCounts.Unverified}, Incomplete: ${statusCounts.Incomplete}`);
// 显示非 Complete 的表单
const nonComplete = Object.entries(record.forms)
.filter(([_, s]) => s.statusLabel !== 'Complete')
.map(([name, s]) => `${name}(${s.statusLabel})`);
if (nonComplete.length > 0 && nonComplete.length <= 5) {
console.log(` 未完成: ${nonComplete.join(', ')}`);
} else if (nonComplete.length > 5) {
console.log(` 未完成: ${nonComplete.slice(0, 5).join(', ')}... (共${nonComplete.length}个)`);
}
}
// ===============================
// 测试 3单个表单状态检查
// ===============================
console.log('\n' + '='.repeat(60));
console.log('📊 测试 3检查 Record 3 的人口学表单状态');
console.log('='.repeat(60));
const demoFormStatus = await adapter.isFormComplete('3', 'basic_demography_form');
console.log(`\n Record 3 - basic_demography_form:`);
console.log(` 状态: ${demoFormStatus.statusLabel} (${demoFormStatus.status})`);
console.log(` 完成: ${demoFormStatus.isComplete ? '是' : '否'}`);
// ===============================
// 测试 4关联分析 - 计算字段 vs 表单状态
// ===============================
console.log('\n' + '='.repeat(60));
console.log('📊 测试 4计算字段与表单状态关联分析');
console.log('='.repeat(60));
// 获取所有记录数据
const allRecords = await adapter.getAllRecordsMerged();
console.log('\n 分析计算字段 "age" 与表单状态的关系:');
console.log(' ' + '-'.repeat(50));
for (const record of allRecords.slice(0, 5)) {
const recId = record.record_id;
const age = record.age;
const dob = record.date_of_birth;
// 获取该记录的表单状态
const statusInfo = formStatus.find(s => s.recordId === recId);
const demoStatus = statusInfo?.forms['basic_demography_form']?.statusLabel || 'Unknown';
const ageDisplay = age !== undefined && age !== '' ? age : '(空)';
console.log(` Record ${recId}: age=${ageDisplay}, dob=${dob || '(空)'}, 表单状态=${demoStatus}`);
}
// ===============================
// 测试 5按事件维度的表单完成状态正确方式
// ===============================
console.log('\n' + '='.repeat(60));
console.log('📊 测试 5按事件维度的表单完成状态 (Record 1)');
console.log('='.repeat(60));
const eventStatus = await adapter.getFormCompletionStatusByEvent('1');
console.log(`\nRecord 1 共有 ${eventStatus.length} 个事件:`);
for (const event of eventStatus) {
const statusIcon = event.eventComplete ? '✅' : '⚠️';
console.log(`\n ${statusIcon} 事件: ${event.eventLabel} (${event.eventName})`);
for (const [formName, status] of Object.entries(event.forms)) {
const icon = status.status === 2 ? '✅' : (status.status === 1 ? '🟡' : '🔴');
console.log(` ${icon} ${formName}: ${status.statusLabel}`);
}
}
// ===============================
// 测试 6获取表单-事件映射
// ===============================
console.log('\n' + '='.repeat(60));
console.log('📊 测试 6表单-事件映射统计');
console.log('='.repeat(60));
const formEventMapping = await adapter.getFormEventMapping();
console.log(`\n表单-事件映射总数: ${formEventMapping.length}`);
// 按事件分组统计
const eventFormCount = new Map<string, number>();
for (const m of formEventMapping) {
eventFormCount.set(m.eventLabel, (eventFormCount.get(m.eventLabel) || 0) + 1);
}
console.log('\n各事件的表单数:');
for (const [event, count] of eventFormCount) {
console.log(` ${event}: ${count} 个表单`);
}
console.log('\n' + '='.repeat(60));
console.log('✅ 测试完成');
console.log('='.repeat(60));
await prisma.$disconnect();
}
main().catch(async (error) => {
console.error('❌ 脚本出错:', error);
await prisma.$disconnect();
process.exit(1);
});