关于自定义函数的概念:自定义函数是应用于 Medidata Rave 项目的代码段,用于扩展自动计算或逻辑核查的功能。也就是说基于Medidata的自定义函数这个模块,用户可以在外部使用C#或者SQL语句来编写相应的代码,与系统中的逻辑核查或派生搭配使用,从而实现一些比较复杂的功能。本篇主要介绍自定义函数基础知识、实际案例和模板库管理三个方面。
01基础知识
在Rave EDC中有Field、Form、Folder等概念,自定义函数中存在着与之一一对应的对象,通过Medidata提供的Core Objects图表可以根据对应关系找到需要使用的对象。
对于一个自定义函数来说,入口一般都是一个数据点“DataPoint”,通过传入的这个数据点,可以从其属性上找到它所有的Query、Comments、便签以及方案偏离等。若从传入的数据点依次向上查找,则可以得到这个数据点对应的Record(记录)、DataPage(页面)、Instance(访视)、Subject(受试者)以及StudySite(中心),此时所有的对照关系都是一对一的,即Record为数据点所在记录,DataPage为记录所在表单,Instance为表单所在访视。
当自定义函数传入一个数据点后,若需要查找其他的某个特定访视,则需从受试者依次向下查找,此时的对照关系均为一对多。即通过传入的数据点查找到当前受试者后,从受试者向下查找Instances、DataPages、Records、DataPoints,此时对应受试者下的所有访视、访视下所有表单、表单下所有记录、记录下所有数据点。
DataPoint、DataPage、Instance分别对应EDC中的字段Filed、表单Form、访视Folder,通过Filed、Form、Folder可以获取字段OID、表单OID和访视OID,此时就可以将自定义函数和EDC中的变量关联起来,根据这种映射关系,可以在自定义函数中查找到所需要的数据。
在实际的临床试验项目中,经常会碰到需要使用自定义函数来实现某些需求的情况,下面是一些较为常见的例子:
①字符串的拼接,如:生成受试者编号
e.g. 受试者编号=中心编号+受试者筛选号
e.g. 按顺序为受试者生成唯一编号
②判断是否有特殊字符
e.g. 受试者的姓名首字母已填写,判断受试者编号是否以姓名首字母开头。
③添加循环访视并设置独立访视名称后缀
e.g. 符合某条件时添加一个循环访视Cn,并根据实际周期设置访视名称后缀n。
④复杂的计算、派生
e.g.计算肌酐清除率时,若为男性,则使用公式A;若为女性,则使用公式B。
⑤通过遍历日志行,确保所有数据点都不满足某条件
e.g. 年龄不在18-75岁范围内,但不满足的入选标准编号/满足的排除标准编号未选择入选标准02。
⑥遍历日志行,找到至少一行满足条件
e.g. 不良事件的转归选择了死亡,但退出试验的主要原因未选择死亡。
⑦将日志行上的数据点与一个或多个其他变量进行匹配
e.g. 不良事件页存在重复记录。
⑧在项目或中心内对全部病人的数据点进行比较
e.g. 对受试者随机号进行查重。
⑨发疑问的情况包含有未提交的页面
e.g.受试者退出试验的主要原因选择不良事件,但“是否发生不良事件”选择“否”或不良事件页未被提交。
⑩向特定用户或角色发送邮件
e.g. 当EDC系统中新记录了一条SAE时,发送相关邮件。
02实际案例
⑴简单实例
Edit Check质疑逻辑:访视日期必须填写。
Edit Check质疑文本:Data is empty, please complete.
假如用Edit Check来实现,则如下图所示:
假如用自定义函数来实现,则如下图所示:
由于传输到自定义函数里的初始值需要是一个数据点,故字段的Data Value需要选择DataPoint;具体的逻辑判断在CF程序中进行,故Check Step里写DataPoint IsPresent即可;Check Action选择Custom Function去调用此CF。
该自定义函数具体每个语句的含义:
语句1:接收Check Action里传输进来的数据点。
语句2:声明一个DataPoint类型的变量,变量名为dp,此时 dp即语句1中传输进来的数据点。
语句3:声明一个布尔型变量,变量名为OpenQuery,初始值为false,用于控制质疑的开和关。
语句4:声明一个字符串变量,变量名为QueryText,变量值即为质疑文本。
语句5:if语句用来判断是否符合发送质疑的条件,当dp值为空时,将布尔型变量OpenQuery的值改为true。
语句6:在自定义函数中用来发送和关闭质疑的固定函数,该函数引用的8个变量分别是:表示质疑文本的字符串、表示Marking Group ID的int型变量(Site from System,Site from CRA,Site from DM等)、布尔值AnswerOnChange(require response)、布尔值CloseOnChange(require manual close)、需要发送质疑的数据点、表示开关质疑动作的布尔值、int型变量CheckID(系统分配的核查编号)和字符串CheckHash(涉及到的数据点)。
⑵实例扩展
Edit Check质疑逻辑:筛选期访视日期不为空,则人口学资料身高必须填写。
Edit Check质疑文本:Screening visit date is not empty, but height is missing, please complete.
假如用自定义函数来实现,则如下图所示:
假如用逻辑核查来实现,则如下图所示:
由于自定义函数中只能传入一个数据点,而我们需要核查两个数据点的值,那么怎样获取到人口学资料-身高的数据呢?具体来看自定义函数代码:
语句1:接收Check Action里传输进来的数据点,此处为筛选期-访视日期。
语句2:声明一个DataPoint类型的变量,变量名为dp,此时 dp即语句1中传输进来的数据点-筛选期-访视日期。
通过dp找到了访视日期所在的记录,再通过这个记录找到访视日期表单,以及访视日期所在的访视。因为我们传进来的访视日期是筛选期的,所以找到的这个访视就是筛选期访视。之后用DataPages类找到了筛选期访视下的所有表单集,从这个表单集中去找表单OID是DM的人口学资料表。
语句3:通过数据点dp查找访视日期所在记录。
语句4:通过记录查找访视日期所在表单。
语句5:通过该表单查找访视日期所在访视。
语句6:通过访视日期所在访视查找该访视下所有表单。
语句7:通过该访视下所有表单利用FindByFormOID函数查找表单OID为”DM”的表单,即人口学资料表。
语句8:通过人口学资料表查找该表下的非日志行记录MasterRecord。
语句9:通过该非日志行记录查找字段OID为”HEIGHT”的数据点,命名为Height。
语句10:声明一个布尔型变量,变量名为OpenQuery,初始值为false,用于控制质疑的开和关。
语句11:声明一个字符串变量,变量名为QueryText,变量值即为质疑文本。
语句12:if语句用来判断是否符合发送质疑的条件,当访视日期的值不为空且身高的值为空时,将布尔型变量OpenQuery的值改为true。
语句13:通过布尔型变量OpenQuery的值控制是否需要开关质疑。
语句14:由于该函数不返回任何值,因此以return null语句作为结束语。
通过上述的例子,可以得到一些检索数据的常见语句:
ActionFunctionParams afp = (ActionFunctionParams)ThisObject;
DataPoint inputDP = afp.ActionDataPoint;
①查找当前受试者 Subject subj = inputDP.Record.Subject;
②查找当前数据点对应的表格 DataPage dpg = inputDP.Record.DataPage;
③查找当前数据点对应的访视 Instance inst = inputDP.Record.DataPage.Instance;
④查找当前受试者的所有访视 Instances insts = inputDP.Record.Subject.Instances;
⑤查找当前Record下的所有数据点 DataPoints dps = inputDP.Record.DataPoints;
⑥查找当前Record下的特定数据点 DataPoint dp = dps.FindByFieldOID("XXX");
⑦查找另外一个特定访视 Instance inst1 = insts.FindByFolderOID("XXX");
⑧查找当前访视下的另外某个表 DataPage dpg1 = inst.DataPages.FindByFormOID("XXX");
⑶实例
①生成受试者编号:受试者编号=中心编号+受试者筛选号
Derivations:
自定义函数代码:
界面展现效果:
②受试者的姓名首字母已填写,判断受试者编号是否以姓名首字母开头,若否,则发送之质疑。
Edit Check:
自定义函数代码:
界面展现效果:
③当前访视“Will the subject continue to the next visit?”选“Yes”时,激活下一个访视“PFS Observation”,同时给新添加的访视补充对应周期的名称后缀,依次循环。
Folders:
Matrices:
Edit Check:
自定义函数代码:
界面展现效果:
④计算筛选期的肌酐清除率时,若为男性,则使用公式A;若为女性,则使用公式B。
Edit Check:
自定义函数代码:
⑤年龄不在18-75岁范围内,但不满足的入选标准编号/满足的排除标准编号未选择入选标准02。
Edit Check:
自定义函数代码:
⑥不良事件的转归选择了死亡,但退出试验的主要原因未选择死亡。
Edit Check:
自定义函数代码:
⑦不良事件页存在重复记录。
Edit Check:
自定义函数代码:
⑧对受试者随机号进行查重。
Edit Check:
自定义函数代码:
⑨受试者退出试验的主要原因选择不良事件,但“是否发生不良事件”选择“否”或不良事件页未被提交。
Edit Check:
自定义函数代码:
⑩当EDC系统中新记录了SAE时,发送邮件。
Edit Check:
自定义函数代码:
邮件展现效果:
03模板库管理
企业内部设有专门的CF模板库文件夹,用于存放现有的自定义函数。模板库中设有目录页,所有程序均按照功能进行分类,并关联至模板库目录页。
模板库文件夹有严格的权限管理控制,对模板库管理员开放可读写权限,管理员主要负责模板库的开发、更新、维护等工作;除管理员外,其他部门成员仅开放只读权限,确保模板库的准确性、安全性。
若在日常工作中遇到一些可以使用自定义函数实现的系统功能,但目前的模板库中尚未涉及,则可由相关人员将需求发送至模板库管理员,由管理员来开发相关程序。若其他部门成员编写了模板库中尚未囊括的自定义函数,需要补充至模板库中时,需要严格遵循以下三个步骤:
1. 部门成员将其编写的自定义函数代码与需要实现的具体功能发送至模板库管理员。
2. 管理员对程序进行审查,如发现程序有bug或有可以删减、完善的部分,对程序进行更新。
3. 在测试项目中进行多方面测试,确保代码完全正确后,将该自定义函数补充至模板库,并告知部门成员。