Variable Table
Syntax
在Airalogy Protocol中,可能存在一种特殊的记录情况,即期望一个{{var}}的值是一个列表,且列表中的每个元素都是一个子Variable,并且列表通常是非定长的。在这种情况下,在Airalogy Protocol的记录前端,其Variable对应的样式将会从一个简单的值变为一个表格。为了支持这种情况,我们可以在var模板中使用subvars参数来定义一个Variable Table。
其基本语法如下:
{{var|<var_id>, subvars=[<subvar_id_1>, <subvar_id_2>, ...]}}当subvars较多时,为了便于阅读,也可以使用多行的方式进行书写,如下:
{{var|<var_id>, subvars=[
<subvar_id_1>,
<subvar_id_2>,
...
]
}}例:
<!-- File: protocol.aimd -->
{{var|testees, subvars=[name, age]}}其在前端展示为一个表格,如下:
| name | age |
|---|---|
| [to_be_filled] | [to_be_filled] |
# File: model.py
from pydantic import BaseModel
class Testee(BaseModel):
# Testee是一个为了实现模型嵌套而定义的中间类。因此其命名实际上可以是任意的,只要保证其与VarModel中list内引用的类名一致即可。根据习惯,我们一般将其命名为对应var_id的PascalCase形式的单数形式。
name: str
age: float
class VarModel(BaseModel):
testees: list[Testee]
# testees即为上述AIMD中的testees,约束testees的数据类型为Testee的列表Variable Table / Sub Variables的标题和描述
当然,如同普通的Variables一般,我们也可以为var及其Sub Variables添加标题和描述。
其方法为在VarModel中使用Field进行相关信息的添加。例:
# File: model.py
from pydantic import BaseModel, Field
class Testee(BaseModel):
name: str = Field(title="Name", description="The name of the testee.")
age: float = Field(title="Age", description="The age of the testee.")
class VarModel(BaseModel):
testees: list[Testee] = Field(title="Testees", description="The testees of the experiment.")当然title和description不是必须的,其可以全无、全有、部分有,均可。
当定义了title和description后,其在前端展示时将会被展示出来。
Assigner for Variable Table
基于Variable Table中的一些Sub Variables赋值另一些Sub Variables
在真实的科研方案中,对于一个Variable Table,其Sub Variables的值也可以通过其他Sub Variables计算得到。为了满足这种依赖的自动计算,我们可以也可以使用Assigner来实现。
例如,var_1中的var_1_2_sum的值可以通过var_1和var_2的值来自动计算获得。对于该实例,我们可以通过分别编写以下三个文件来实现:
文件1:AIMD文件。在该文件中,我们显式定义了var_1。
<!-- File: protocol.aimd -->
{{var|var_1, subvars=[var_1, var_2, var_1_2_sum]}}文件2:Model文件。在该文件中,我们定义了var_1的数据类型。
# File: model.py
from pydantic import BaseModel
class VarTable1(BaseModel):
var_1: int
var_2: int
var_1_2_sum: int
class VarModel(BaseModel):
var_1: list[VarTable1]文件3:Assigner文件。在该文件中,我们定义了var_1中的var_1_2_sum的计算逻辑。
# File: assigner.py
from airalogy.assigner import AssignerResult, assigner
@assigner(
assigned_fields=[
"var_1.var_1_2_sum",
],
dependent_fields=[
"var_1.var_1",
"var_1.var_2",
], # 注意,当Variable Table参与Assigner计算时,其assigned_fields和dependent_fields的名称需要加上Variable Table的名称前缀,并且最多来源于一个Variable Table。不得跨Variable Table进行计算
mode="auto",
)
def calculate_var_1(dependent_fields: dict) -> AssignerResult:
var_1 = dependent_fields["var_1.var_1"]
var_2 = dependent_fields["var_1.var_2"]
var_1_2_sum = var_1 + var_2
return AssignerResult(
assigned_fields={
"var_1.var_1_2_sum": var_1_2_sum,
},
)在使用Variable Table Assigner时,我们需要注意以下几点:
- Variable Table中,由于每一行的数据是独立的,因此Variable Table Assigner的计算逻辑也是基于每一行的数据进行计算的。不同行之间的数据不能相互影响。
- Variable Table的Sub Variables的值是以列的形式存在的,用户在填写Variable Table时,通常是一行一行的填写,因此在自动计算的时候为了节约计算资源,我们在前端监听用户填写的行,当此行所有的
dependent_fields都填写完毕时,才会触发Variable Table Assigner的计算逻辑,并计算获得此行的assigned_fields的值。对于其他行,我们不会进行计算,直到用户填写完毕。
将整个Variable Table作为Dependent Field
Variable Table的一种常见用法是用于批量的参数设置。例如,如果我们想要使用一个Variable Table来设置多个和绘制图表相关的参数,我们可以将整个Variable Table作为一个Dependent Field,然后在Assigner中对整个Variable Table进行计算。
model.py:
class VarTable1(BaseModel):
font_size: int
font_color: str
class VarModel(BaseModel):
var_table_1: list[VarTable1]
font_config_summary: str则如果我们记录了以下的Variable Table的数据:
| font_size | font_color |
|---|---|
| 12 | red |
| 14 | blue |
则其对应的JSON数据如下:
{
"var_table_1": [
{
"font_size": 12,
"font_color": "red"
},
{
"font_size": 14,
"font_color": "blue"
}
],
}为此,我们可以编写以下的Assigner来计算font_config_summary:
assigner.py:
from airalogy.assigner import AssignerResult, assigner
@assigner(
assigned_fields=[
"font_config_summary",
],
dependent_fields=[
"var_table_1",
],
mode="auto",
)
def calculate_font_config_summary(dependent_fields: dict) -> AssignerResult:
font_config_summary = "\n".join(
[
f"font_size: {row['font_size']}, font_color: {row['font_color']}"
for row in dependent_fields["var_table_1"]
]
)
return AssignerResult(
assigned_fields={
"font_config_summary": font_config_summary,
},
)当然,我们也可以把一个Variable Table作为一个Assigned Field作为Assigner的传出。注意此时,每次Assigner的计算都会返回一个完整的Variable Table,而不是单行的Variable Table。因此如果Variable Table已经有数据了,则此种情况下会覆盖原有的Variable Table数据。注意,当将整个Variable Table作为Assigned Field时,不支持再在此基础上进行基于行的Assigner计算,因为本质上既然都能够返回整个Variable Table了,那么就不需要再进行行级的计算了(因为可以在整个Table计算的过程中将行计算的逻辑也包含在内)。