165 lines
6.0 KiB
TypeScript
165 lines
6.0 KiB
TypeScript
/**
|
||
* 测试脚本:验证表单状态和计算字段获取
|
||
*/
|
||
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);
|
||
});
|