质性编码中的分析备忘录
Potato 的备忘录可附在条目、一段文本或一个绘制的形状上,并可接受回复。作为议题提出的备忘录会一直保持未解决状态,直到有人解决该议题。
Potato 中的备忘录是附在条目、条目内的一段文本选区或图像上绘制的形状上的自由文本笔记。任何能读到某条备忘录的用户都可以回复这条备忘录;作为议题(issue)提出的备忘录会一直处于未解决状态,直到作者、被指派人或裁定者解决该议题。备忘录适用于任何 Potato 项目,无论是否启用 QDA 模式,Potato 都把备忘录保存在项目数据库中,而不是标注文件里。备忘录在 Potato 2.6.0 中加入,回复和议题在 2.12.0 中加入。
分析备忘录是研究者在编码过程中针对数据和代码写下的笔记,例如某个代码为何适合某段文字,或码本哪里需要一条新规则。撰写备忘录最常被描述为扎根理论的一部分,Birks、Chapman 和 Francis(2008) 则建议在各种质性方法中都撰写备忘录。按照这几位作者的说法,备忘录帮助分析者从原始数据走向解释这些数据的抽象概念。在编码团队中,备忘录还记录了代码背后的决定,下文介绍的导出会把这些笔记变成一份审计轨迹。
每条备忘录记录以下内容:
- 锚点。 备忘录附在整个条目上、条目文本的一个字符范围上(显示为
quote徽标),或一个绘制的形状上(显示为该形状的标签和类型,例如car box)。 - 可见性。 备忘录为
private(默认)或shared。 - 讨论串。 回复排在第一条笔记之下,并沿用第一条笔记的可见性。
- 议题状态。 议题为
open或resolved,可以有一名被指派人,并记录由谁在何时解决了该议题。 - 存储。 点击 Add note 后备忘录即被保存,存放在
<task_dir>/project.sqlite的memos和memo_threads表中。
启用备忘录
标准项目中备忘录默认关闭,在 QDA 模式和单人模式下默认开启。项目通过 annotation_ui 代码块启用备忘录。下面的配置为一个团队设置了用 span 方案编码访谈摘录的任务,并使用私有备忘录。adjudication 代码块指定了一名首席分析者,裁定者可以读到项目中的每一条备忘录:
annotation_task_name: Clinic access interviews
task_dir: .
output_annotation_dir: annotation_output/
data_files:
- data/excerpts.json
item_properties:
id_key: id
text_key: text
annotation_ui:
memos: true
visibility: private
adjudication:
enabled: true
adjudicator_users: [lead_analyst]
annotation_schemes:
- annotation_type: span
name: codes
description: Highlight a passage and apply a code
labels: [transport barriers, cost concerns, provider trust]span 方案对导出很重要,因为引文报告需要一个 span 方案(见导出备忘录)。有四个键控制备忘录:
| 选项 | 默认值 | 说明 |
|---|---|---|
annotation_ui.memos | 关闭;当 qda_mode.enabled 或 solo_mode.enabled 为 true 时开启 | 显示 Notes 面板并启用 /api/memos 端点。显式的 true 或 false 会覆盖 QDA 模式和单人模式的默认值。 |
annotation_ui.visibility | private | 通过 API 创建、未带 visibility 字段的备忘录所用的可见性。private 或 shared 以外的任何值都会回退为 private。 |
qda_mode.memos.show_sidebar_by_default | true | 在 QDA 模式下,在每个条目上打开 Notes 面板,直到标注者关闭该面板。 |
qda_mode.memos.enabled | true | 在 QDA 模式下,设为 false 会让 Notes 面板一开始保持关闭。备忘录仍然可用;关闭备忘录的键是 annotation_ui.memos: false。 |
在 QDA 模式之外,Notes 面板一开始是关闭的,标注者通过页面右边缘的 Notes 按钮打开面板。关闭面板后,在该浏览器标签页会话的剩余时间里,后续条目上的面板也保持关闭。备忘录关闭时,Potato 不渲染 Notes 按钮,/api/memos 端点返回 503。在每个项目中,编辑框的可见性菜单都从 Private 开始,annotation_ui.visibility 不会改变这个菜单。希望使用共享备忘录的团队,需要请编码者为每条新备忘录选择 Shared。
Potato 把每条备忘录归档在项目的 annotation_task_name 之下。在配置中给任务改名后,旧名称下写的备忘录就会被隐藏。这些记录仍留在 project.sqlite 中,恢复旧名称后备忘录会重新出现。
在 Notes 面板中撰写备忘录
Notes 面板按从旧到新的顺序列出当前条目上的备忘录,每个讨论串的回复排在第一条笔记之下。添加备忘录的步骤:
- 点击标注页面右边缘的 Notes。
- 要把备忘录锚定到一段文字,先在条目中选中文本,再勾选 Attach to selected text。要把备忘录锚定到一个绘制的形状,先在图像画布上选中该形状,再勾选 Attach to the selected shape。
- 在编辑框中输入笔记。
- 要把笔记作为议题提出,勾选 Raise as an issue to resolve。也可以在 Assign to 下输入应当解决该议题的人的用户名。
- 在 Visibility 下选择 Private 或 Shared,然后点击 Add note。
每条备忘录只显示当前用户可以使用的按钮。Reply 出现在每个讨论串上,Resolve 或 Reopen 以及 Assign 出现在该用户有权更改的议题上,Edit 和 Delete 出现在该用户自己的备忘录上。裁定者还会在其他用户的备忘录上看到 Delete。编辑第一条笔记时,除了文本,还可以更改笔记的可见性。
文本锚点记录选区在条目显示文本中的起止字符偏移量,结束偏移量不包含在内。只有在条目的主文本显示区域内的选区才会被计入。形状锚点记录形状的方案、类型、标签和归一化坐标,因为绘制的形状没有存储的 ID。点击备忘录上的形状徽标,会通过匹配存储的坐标重新选中该形状。如果形状此后被移动或删除,面板会提示该形状已不在图像上原来绘制的位置。
谁能读到备忘录
私有备忘录只有作者和裁定者能读到。共享备忘录项目中的每个用户都能读到。议题的被指派人即使在议题为私有时,也能读取和回复该议题的讨论串。备忘录的其余规则如下:
- 只有作者可以编辑备忘录的文本或可见性。
- 作者或裁定者可以删除备忘录。删除讨论串的第一条笔记会同时删除其回复。
- 任何能读到讨论串的人都可以回复。回复沿用第一条笔记的可见性,之后对第一条笔记可见性的更改也会作用于回复。
- 对于用户无法读取的备忘录,每个操作都返回
404。Potato 对不存在的备忘录给出同样的响应。
对备忘录而言,裁定者是指在 adjudication.enabled 为 true 时列在 adjudication.adjudicator_users 中的用户名。Potato 在处理备忘录时只检查这份名单。持有 RBAC admin 角色但不在名单上的用户,读到的备忘录与普通标注者相同。
私有备忘录只在标注界面内是私有的。任何运行引文报告导出或打开 project.sqlite 的人都能读到每一条备忘录,包括私有备忘录。请在编码开始前告诉编码者这项导出。
共享备忘录让编码者在编码时能读到彼此的推理。当两名编码者为编码者间信度检验独立编码同一批摘录时,共享备忘录可能锚定第二名编码者的判断,并因与码本无关的原因抬高一致性。在信度轮次中请保持备忘录私有。该轮结束后,编码者可以用 Edit 打开每条私有备忘录,并将其切换为 Shared,供协调会议使用。
回复与议题
每条备忘录都会开启一个讨论串。回复没有独立于第一条笔记的可见性或状态。勾选 Raise as an issue to resolve 会让第一条笔记的状态变为 open。议题适合那些需要在继续编码前得到回答的问题,例如码本没有界定清楚的代码边界。议题的权限如下:
| 操作 | 谁可以执行该操作 |
|---|---|
| 回复 | 任何能读到该讨论串的人 |
| 解决或重新打开议题 | 议题作者、被指派人或裁定者 |
| 指派议题或清除被指派人 | 议题作者或裁定者 |
解决议题会记录由谁在何时解决,重新打开议题会清空这两个字段。清除指派会撤销被指派人对私有议题的访问权限。当有未解决的议题指派给某位标注者时,Notes 按钮会显示数量,例如 Notes · 2 assigned to you,面板也会把这些议题列在顶部。位于该标注者自己队列中的条目上的议题会带有 Go to item 按钮,不在队列中的条目上的议题则标为不在队列中。设置 jumping_to_id_disabled: true 会移除 Go to item 按钮。
要把指派信息发送到聊天频道或事务跟踪系统,请为 memo.issue_assigned 事件订阅一个 webhook。每当议题有了新的被指派人,该事件就会触发,并发送备忘录 ID、条目 ID、被指派人、执行指派的用户和状态。由于议题可能是私有的,载荷中不包含备忘录的文本。
管理仪表板中的议题
备忘录开启时,管理仪表板的 Overview 标签页会显示一个 Open issues 卡片,列出未解决议题的数量和已解决的数量。管理 API 会列出议题本身,包括私有议题:
curl -H "X-API-Key: $POTATO_ADMIN_KEY" \
"http://localhost:8000/admin/api/memo_issues?status=open"响应中每个议题对应一项,按从新到旧排列,包含备忘录的文本、作者、条目 ID、锚点、被指派人、状态和解决人。使用 ?status=resolved 获取已解决的议题,省略该参数则同时返回两类。该端点只列出议题。普通备忘录要通过导出或裁定者账户才能到达项目负责人手中。
导出备忘录
quotation_report 导出器为每个已编码的跨度写一行 CSV。加上 include_memos=true 后,导出器会为每条备忘录和每条回复各加一行:
python -m potato.export -c config.yaml -f quotation_report \
--option include_memos=true -o ./export/该命令会写出 ./export/quotations.csv。备忘录行与跨度行使用相同的九列:
| 列 | 备忘录行中的值 |
|---|---|
schema | (memo)、(memo reply)、(issue: open) 或 (issue: resolved) |
code | 讨论串的可见性,private 或 shared |
text | 备忘录的文本 |
start, end | 文本锚点的字符偏移量,其他情况为空 |
field | 文本锚点为 text,其他情况为空 |
instance_id | 备忘录所在的条目 |
source_doc | 条目文本的前 200 个字符 |
coder | 备忘录的作者 |
只有当配置中至少有一个 span 方案时,该导出器才会运行。只用 radio 或 multiselect 题目编码的研究会得到错误 No span annotation schema found in config。备忘录行也没有时间戳和被指派人,锚定在形状上的备忘录导出时偏移量为空。
如需带日期的审计轨迹,请直接查询两张备忘录表。下面的 sqlite3 命令列出项目中的每条备忘录,包括 UTC 创建时间、讨论串状态、被指派人和解决人:
sqlite3 -header -csv project.sqlite "
SELECT m.instance_id, m.created_by, datetime(m.created_at, 'unixepoch') AS created,
m.visibility, t.parent_id, t.status, t.assignee, t.resolved_by, m.body
FROM memos m LEFT JOIN memo_threads t ON t.memo_id = m.id
WHERE m.project = 'Clinic access interviews'
ORDER BY m.created_at" > memos.csv非空的 parent_id 表示这是一条回复,并指明讨论串的第一条笔记。Potato 以 Unix 秒存储时间,resolved_at 和 updated_at 的转换方式与 created_at 相同。
备忘录不会进入 REFI-QDA .qdpx 导出。当配置设置了 annotation_ui.memos: true 时,.qdpx 导出器会警告备忘录被略去。在未设置该键的 QDA 模式或单人模式下,导出会略去备忘录且不发出警告。项目迁移到其他 QDA 程序时,请用引文报告单独导出备忘录。
potato snapshot create 构建的归档包含 project.sqlite,potato deploy pull 生成的归档同样包含该文件。因此这两种归档都保存了每一条备忘录,包括私有备忘录。快照的内容哈希覆盖配置、研究的文件和标注。哈希不包括数据库,新增备忘录不会改变哈希。参见项目快照。
备忘录 API
Notes 面板使用 /api/memos 下的 REST API,通过标注者的登录会话认证。脚本也可以调用同样的端点:
| 请求 | 用途 |
|---|---|
GET /api/memos?instance_id=<id> | 某个条目上该用户可读的备忘录和回复,每项带有 can_edit、can_delete、can_resolve 和 can_assign |
POST /api/memos | 由 instance_id、body、visibility 和 anchor 创建新备忘录;议题另加 issue: true 和可选的 assignee |
POST /api/memos/<id>/reply | 带 body 的回复 |
PATCH /api/memos/<id> | 更改 body、visibility、status(open 或 resolved)或 assignee(留空即清除) |
DELETE /api/memos/<id> | 删除备忘录;若该备忘录是第一条笔记,则连同回复一起删除 |
GET /api/memos/issues?status=open&assignee=me | 整个项目中该用户可读的议题 |
文本锚点形如 {"start": 48, "end": 98, "field": "text"},形状锚点形如 {"shape": {"schema": ..., "type": ..., "label": ..., "coordinates": ...}}。对不在项目中的条目 ID 发送 POST 会返回 400。
示例
Potato 的仓库附带一个备忘录示例。在 Potato 代码检出目录中运行该示例:
python potato/flask_server.py start examples/advanced/memos-example/config.yaml -p 8000该示例在四条简短的产品评论上启用私有备忘录,并配有一道情感题目。该示例没有 span 方案,引文报告无法导出该示例的备忘录。QDA 模式示例带有以码本为支撑的 span 方案。要试用导出,请运行 examples/advanced/qda-mode-example/config.yaml。
延伸阅读
- QDA 模式:QDA 模式与备忘录一同启用的码本、案例和搜索
- 质性研究中的编码者间信度:何时让备忘录保持私有
- 导出格式:完整的导出器参考
- 管理仪表板:API 密钥和 Overview 标签页
- Webhooks:投递
memo.issue_assigned事件
有关实现细节,请参见源文档。
参考文献
Birks, M., Chapman, Y., and Francis, K. (2008). Memoing in qualitative research: Probing data and processes. Journal of Research in Nursing, 13(1), 68–75. doi:10.1177/1744987107081254