一个命令行工具(和Python库)用于从CommCare HQ提取数据到SQL数据库或Excel工作簿
项目描述
CommCare Export
https://github.com/dimagi/commcare-export
一个命令行工具(和Python库)用于从CommCare HQ REST API生成定制导出。
安装和快速开始
以下命令应在终端或命令行上运行。
在终端窗口或命令行中,为了简单起见,从主目录运行命令。
Python
检查已安装的Python版本。
此工具已在3.8到3.12版本的Python上进行了测试。
$ python3 --version
如果已安装Python,将显示其版本。
如果未安装Python,请下载并安装3.8到3.12版本的Python。
Virtualenv(可选)
建议为CommCare Export设置虚拟环境,以避免与其他Python应用程序发生冲突。
有关virtualenv的更多信息,请参阅https://docs.pythonlang.cn/3/tutorial/venv.html
使用以下命令设置虚拟环境:
$ python3 -m venv venv
通过运行以下命令激活虚拟环境:
$ source venv/bin/activate
注意:每次启动新的终端会话或命令行提示符时,都需要激活virtualenv。
为了方便,为了避免这样做,您可以在"venv"目录中创建一个别名来激活虚拟环境,只需将以下内容添加到您的.bashrc
或.zshrc
文件中
$ alias venv='if [[ -d venv ]] ; then source venv/bin/activate ; fi'
然后您可以通过简单地输入以下内容来激活虚拟环境
$ venv
安装CommCare Export
通过pip
安装CommCare Export
$ pip install commcare-export
CommCare HQ
-
如果您尚未注册,请注册CommCare HQ。
-
创建一个项目空间和应用程序。
-
访问发布管理器,创建构建,点击星星以发布。
-
使用Web应用程序并填写一些表单。
-
修改
examples/
目录中的一个示例查询,将“过滤器值”列修改为与您的表单XMLNS/案例类型匹配。请参阅此页面以确定表单的XMLNS。
现在您可以运行以下示例
$ commcare-export \
--query examples/demo-registration.xlsx \
--project YOUR_PROJECT \
--output-format markdown
$ commcare-export \
--query examples/demo-registration.json \
--project YOUR_PROJECT \
--output-format markdown
$ commcare-export \
--query examples/demo-deliveries.xlsx \
--project YOUR_PROJECT \
--output-format markdown
$ commcare-export \
--query examples/demo-deliveries.json \
--project YOUR_PROJECT \
--output-format markdown
您将看到打印的表。将更改到--output-format sql --output URL_TO_YOUR_DB --since DATE
以同步自该日期以来提交的所有表单。
示例查询文件以Excel和JSON格式提供。建议使用Excel格式,因为JSON格式可能会在未来库版本中更改。
命令行使用
命令行工具的基本用法是与保存的Excel或JSON查询一起使用(下面将说明如何编写这些内容)
$ commcare-export --commcare-hq <URL or alias like "local" or "prod"> \
--username <username> \
--project <project> \
--api-version <api version, defaults to latest known> \
--version <print current version> \
--query <excel file, json file, or raw json> \
--output-format <csv, xls, xlsx, json, markdown, sql> \
--output <file name or SQL database URL> \
--users <export data about project's mobile workers> \
--locations <export data about project's location hierarchy> \
--with-organization <export users, locations and joinable form or case tables>
有关完整选项列表,请参阅commcare-export --help
在examples/
目录中提供了CommCare演示应用程序的示例查询文件(可在CommCare HQ Exchange中找到)。
--output
CommCare Export使用SQLAlchemy的create_engine来建立数据库连接。这是基于RFC-1738协议的。一些常见示例
# Postgres
postgresql+psycopg2://scott:tiger@localhost/mydatabase
# MySQL
mysql+pymysql://scott:tiger@localhost/mydatabase
# MSSQL
mssql+pyodbc://scott:tiger@localhost/mydatabases?driver=ODBC+Driver+17+for+SQL+Server
Excel查询
Excel查询是任何.xlsx
工作簿。工作簿中的每个工作表代表您希望创建的一个表。有两个分组列可以配置表格
- 数据源:将其设置为
form
以导出表单数据,或case
以导出案例数据。 - 过滤器名称 / 过滤器值:这些列配对以过滤输入案例或表单。
- 字段:您的SQL数据库中的目标。
- 源字段:您希望从中提取的特定字段。这可以是任何JSON路径。
JSON查询
下面的表格中描述了JSON查询。您构建一个表示您所想查询的JSON对象。一种良好的入门方法是使用示例,或者您也可以创建一个Excel查询,并使用--dump-query
运行工具以查看生成的JSON查询。
用户和位置数据
使用--users和--locations选项可以导出CommCare项目中的数据,这些数据可以与表单和案例数据一起使用。使用--with-organization选项执行所有这些操作,并在Excel查询规范中添加一个字段以进行连接。
指定--users选项或--with-organization选项将导出一个名为'commcare_users'的额外表,包含以下列
列 | 类型 | 说明 |
---|---|---|
id | 文本 | 主键 |
default_phone_number | 文本 | |
电子邮件 | 文本 | |
first_name | 文本 | |
groups | 文本 | |
last_name | 文本 | |
phone_numbers | 文本 | |
resource_uri | 文本 | |
commcare_location_id | 文本 | 外键到commcare_locations |
commcare_location_ids | 文本 | |
commcare_primary_case_sharing_id | 文本 | |
commcare_project | 文本 | |
用户名 | 文本 |
'commare_users'表中的数据来自列表移动工作者API端点。
指定 --locations 选项或 --with-organization 选项将导出一个名为 'commcare_locations' 的额外表格,包含以下列
列 | 类型 | 说明 |
---|---|---|
id | 文本 | |
created_at | 日期 | |
域 | 文本 | |
外部ID | 文本 | |
last_modified | 日期 | |
纬度 | 文本 | |
位置数据 | 文本 | |
位置ID | 文本 | 主键 |
位置类型 | 文本 | |
经度 | 文本 | |
名称 | 文本 | |
父级 | 文本 | 父级位置的资源URI |
resource_uri | 文本 | |
站点代码 | 文本 | |
行政位置类型 | 文本 | |
位置类型代码 | 文本 | |
位置类型名称 | 文本 | |
位置类型父级 | 文本 | |
位置级别代码 | 文本 | 列名取决于项目的组织 |
位置级别代码 | 文本 | 列名取决于项目的组织 |
'commcare_locations' 表中的数据来自位置API端点,以及来自位置类型API端点的某些附加列。如果为项目设置了组织级别,则表中最后几列存在。为每个组织级别创建一个列。列名来自您指定的位置类型。列值是您组织中该级别的包含位置的位置ID。考虑来自 CommCare帮助页面 的示例组织。'commcare_locations' 表的一个片段可能如下所示
位置ID | 位置类型名称 | chw | 主管 | 诊所 | 地区 |
---|---|---|---|---|---|
939fa8 | 地区 | NULL | NULL | NULL | 939fa8 |
c4cbef | 诊所 | NULL | NULL | c4cbef | 939fa8 |
a9ca40 | 主管 | NULL | a9ca40 | c4cbef | 939fa8 |
4545b9 | CHW | 4545b9 | a9ca40 | c4cbef | 939fa8 |
为了将表单或案例数据与 'commcare_users' 和 'commcare_locations' 连接起来,导出的表单和案例需要包含一个字段,用于标识提交它们的用户。--with-organization 选项会自动为 Excel 规范中的每个查询添加一个名为 'commcare_userid' 的字段,以便进行此操作。使用该字段,您可以使用带有连接的 SQL 查询来报告有关您组织中任何级别的数据。例如,要统计每个诊所所有工作人员提交的表单数量
SELECT l.clinic,
COUNT(*)
FROM form_table t
LEFT JOIN (commcare_users u
LEFT JOIN commcare_locations l
ON u.commcare_location_id = l.location_id)
ON t.commcare_userid = u.id
GROUP BY l.clinic;
请注意,'commcare_users' 和 'commcare_locations' 表名被视为保留名称,并且如果给出将数据写入其中任何一个的查询规范,导出工具将产生错误。
导出工具会将所有用户写入 'commcare_users',并将所有位置写入 'commcare_locations',用当前数据覆盖现有行,并为新用户和位置添加行。如果您想从表中删除过时的用户或位置,请删除它们,下一次导出将仅保留当前的。如果您修改组织以添加或删除级别,您将更改 'commcare_locations' 表的列,并且您很可能在用新组织导出之前删除该表。
安排 DET 计划任务
安排 DET 在常规时间间隔内运行是保持您的数据库与 CommCare HQ 保持同步的有用策略。
安排 DET 运行的一种常见方法是在操作系统的调度库中调用脚本以执行 commcare-export
命令。可以在 examples/
目录中找到 Windows 和 Linux 的示例脚本。
Windows
在 Windows 系统上,您可以使用 任务计划程序 运行计划脚本。
examples/
目录包含一个示例脚本文件 scheduled_run_windows.bat
,任务计划程序可以使用它来调用 commcare-export
命令。
要设置计划任务,您可以按照以下步骤操作。
- 将文件
scheduled_run_windows.bat
复制到系统上的任何所需位置(例如文档
) - 编辑复制的
.bat
文件并填写您自己的详细信息 - 按照 此处 提供的步骤进行操作,当提示输入
程序/脚本
时使用 .bat 文件。
Linux
在 Linux 系统上,您可以使用 crontab 命令在系统中创建计划操作(cron作业)。
《examples/》目录包含一个示例脚本文件,scheduled_run_linux.sh
,可以被cron作业使用。要设置cron作业,请按照以下步骤进行。
- 将示例文件复制到主目录
cp ./examples/scheduled_run_linux.sh ~/scheduled_run_linux.sh
- 编辑文件以填充您的详细信息
nano ~/scheduled_run_linux.sh
- 通过追加到crontab文件来创建cron作业
crontab -e
在任意现有cron作业下添加条目。以下示例在每天的每12个小时顶部执行脚本文件
0 12 * * * bash ~/scheduled_run_linux.sh
您可以使用crontab.guru工具,这是一个非常有用的工具,可以生成和解释任何自定义cron计划。
Python库使用
作为库,各种commcare_export
模块使得
- 与CommCare HQ REST API交互变得容易
- 对API执行"Minilinq"查询(一种非常简单的查询语言,如下所述)
- 加载和保存Minilinq查询的JSON表示
- 将Excel配置编译为Minilinq查询
直接访问CommCare HQ REST API
from commcare_export.checkpoint import CheckpointManagerWithDetails
from commcare_export.commcare_hq_client import CommCareHqClient, AUTH_MODE_APIKEY
from commcare_export.commcare_minilinq import get_paginator, PaginationMode
username = 'some@username.com'
domain = 'your-awesome-domain'
hq_host = 'https://commcarehq.org'
API_KEY= 'your_secret_api_key'
api_client = CommCareHqClient(hq_host, domain, username, API_KEY, AUTH_MODE_APIKEY)
case_paginator=get_paginator(resource='case', pagination_mode=PaginationMode.date_modified)
case_paginator.init()
checkpoint_manager=CheckpointManagerWithDetails(None, None, PaginationMode.date_modified)
cases = api_client.iterate('case', case_paginator, checkpoint_manager=checkpoint_manager)
for case in cases:
print(case['case_id'])
对其执行minilinq
查询,然后以JSON序列化的形式打印该查询
import json
import sys
from commcare_export.minilinq import *
from commcare_export.commcare_hq_client import CommCareHqClient
from commcare_export.commcare_minilinq import CommCareHqEnv
from commcare_export.env import BuiltInEnv, JsonPathEnv
from commcare_export.writers import StreamingMarkdownTableWriter
api_client = CommCareHqClient(
url="http://www.commcarehq.org",
project='your_project',
username='your_username',
password='password',
version='0.5'
)
source = Map(
source=Apply(
Reference("api_data"),
Literal("form"),
Literal({"filter": {"term": {"app_id": "whatever"}}})
),
body=List([
Reference("received_on"),
Reference("form.gender"),
])
)
query = Emit(
'demo-table',
[
Literal('Received On'),
Literal('Gender')
],
source
)
print(json.dumps(query.to_jvalue(), indent=2))
results = query.eval(BuiltInEnv() | CommCareHqEnv(api_client) | JsonPathEnv())
if len(list(env.emitted_tables())) > 0:
with StreamingMarkdownTableWriter(sys.stdout) as writer:
for table in env.emitted_tables():
writer.write_table(table)
这将输出与以下等效的JSON
{
"Emit": {
"headings": [
{
"Lit": "Received On"
},
{
"Lit": "Gender"
}
],
"source": {
"Map": {
"body": {
"List": [
{
"Ref": "received_on"
},
{
"Ref": "form.gender"
}
]
},
"name": null,
"source": {
"Apply": {
"args": [
{
"Lit": "form"
},
{
"Lit": {
"filter": {
"term": {
"app_id": "whatever"
}
}
}
}
],
"fn": {
"Ref": "api_data"
}
}
}
}
},
"table": "demo-table"
}
}
MiniLinq参考
抽象语法可以直接在commcare_export.minilinq
模块中检查。请注意,函数和原语之间的选择是为了故意展示MiniLinq的结构,以便进行可能的优化,并限制整体语言。
以下是关于抽象语法和语义的描述
Python | JSON | 它评估为 |
---|---|---|
Literal(v) |
{"Lit": v} |
仅v |
Reference(x) |
{"Ref": x} |
在环境中解析为x 的任何内容 |
List([a, b, c, ...]) |
{"List": [a, b, c, ...} |
由a ,b ,c 评估得到的内容列表 |
Map(source, name, body) |
{"Map": {"source": ..., "name": ..., "body": ...} |
对source 中的每个元素评估body 。如果提供了name ,则将元素绑定到它,否则它将替换整个环境。 |
FlatMap(source, name, body) |
{"FlatMap": {"source" ... etc}} |
映射后扁平化,类似于嵌套列表推导式 |
Filter(source, name, body) |
等等 | |
Bind(value, name, body) |
等等 | 当评估body 时,将value 的结果绑定到name |
Emit(table, headings, rows) |
等等 | 发出带有headings 和rows 的table 。注意,table 是一个字符串,headings 是一个表达式列表,而rows 是一个表达式列表的列表。请参见下面的输出说明。 |
Apply(fn, args) |
等等 | 将fn 评估为函数,并将所有args 应用于该函数。 |
通过环境提供内置函数,如api_data
和基本算术和比较,通过使用Ref
以名称引用,并通过Apply
使用。
内置函数列表
函数 | 描述 | 示例使用 |
---|---|---|
+, -, *, //, /, >, <, >=, <= |
标准数学 | |
len | 长度 | |
bool | 布尔值 | |
str2bool | 将字符串转换为布尔值。真值是'true','t','1'(不区分大小写) | |
str2date | 将字符串转换为日期 | |
bool2int | 将布尔值转换为整数(0,1) | |
str2num | 将字符串解析为数字 | |
format-uuid | 解析十六进制UUID,并格式化为带连字符的组 | |
substr | 返回通过[第一个参数,第二个参数)索引的子串,零索引。 | substr(2, 5) of 'abcdef' = 'cde' |
selected-at | 返回字符串中的第N个单词。N是零索引。 | selected-at(3) - 返回第4个单词 |
selected | 如果给定的单词在值中,则返回 True。 | selected(发热) |
count-selected | 计算单词数量 | |
json2str | 将 JSON 对象转换为字符串 | |
template | 渲染字符串模板(不稳健) | template({}, on {}, state, date) |
attachment_url | 将附件名称转换为下载 URL | |
form_url | 输出 CommCare HQ 上表单视图的 URL | |
case_url | 输出 CommCare HQ 上案例视图的 URL | |
unique | 输出列表中的唯一值 |
输出格式
您的 MiniLinq 可以通过使用 Emit
表达式定义多个包含标题的表格,以及它们的内容行,或者简单地返回单个查询的结果。
如果您的 MiniLinq 不包含任何 Emit
表达式,那么表达式的结果将以格式化的 JSON 打印到标准输出。
如果您的 MiniLinq 确实 包含 Emit
表达式,则有许多格式可用,通过 --output-format <format>
选项选择,并且可以使用 --output <file>
命令行选项将其输出到文件。
csv
:每个表格都将是一个 Zip 存档内的 CSV 文件。xls
:每个表格都将是一个旧格式 Excel 电子表格中的工作表。xlsx
:每个表格都将是一个新格式 Excel 电子表格中的工作表。json
:每个表格都将是一个 JSON 字典的成员,打印到标准输出。markdown
:表格将以 Markdown 格式流到标准输出(非常适合调试查询)。sql
:所有数据都将被“插入或更新”到您指定的 SQL 数据库中,包括创建所需的表和列。
依赖项
所需的依赖项将通过 pip 自动安装。但由于您可能不关心所有导出格式,因此这些依赖项是可选的。以下是安装方法:
# To export "xlsx"
$ pip install "commcare-export[xlsx]"
# To export "xls"
$ pip install "commcare-export[xls]"
# To sync with a Postgres database
$ pip install "commcare-export[postgres]"
# To sync with a mysql database
$ pip install "commcare-export[mysql]"
# To sync with a database which uses odbc (e.g. mssql)
$ pip install "commcare-export[odbc]"
# To sync with another SQL database supported by SQLAlchemy
$ pip install "commcare-export[base_sql]"
# Then install the Python package for your database
贡献
0. 如果您尚未注册,请前往 https://github.com 注册 GitHub。
1. 在 https://github.com/dimagi/commcare-export 上叉取存储库。
2. 克隆您的叉取,安装到虚拟环境,并开始一个功能分支
$ git clone git@github.com:dimagi/commcare-export.git
$ cd commcare-export
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install -e ".[test]"
$ git checkout -b my-super-duper-feature
3. 进行编辑。
4. 确保测试通过。测试所有版本的最佳方法是注册 https://travis-ci.org 并为您的叉取打开自动持续测试。
$ py.test
=============== test session starts ===============
platform darwin -- Python 2.7.3 -- pytest-2.3.4
collected 17 items
tests/test_commcare_minilinq.py .
tests/test_excel_query.py ....
tests/test_minilinq.py ........
tests/test_repeatable_iterator.py .
tests/test_writers.py ...
============ 17 passed in 2.09 seconds ============
5. 在 env
和 minilinq
模块中使用类型提示。请确保这些模块中的任何更改都遵循这些类型。
$ mypy --install-types @mypy_typed_modules.txt
6. 将功能分支推上去
$ git push -u origin my-super-duper-feature
7. 访问 https://github.com/dimagi/commcare-export 并提交一个拉取请求。
接受我们对您的贡献的感激之情:谢谢!
发布流程
1. 为发布创建标签
$ git tag -a "X.YY.0" -m "Release X.YY.0"
$ git push --tags
2. 创建源分发
$ python setup.py sdist
确保存档(dist/commcare-export-X.YY.0.tar.gz
)具有正确的版本号(与标签名称匹配)。
3. 上传到 pypi
$ pip install twine
$ twine upload -u dimagi dist/commcare-export-X.YY.0.tar.gz
验证上传
https://pypi.python.org/pypi/commcare-export
5. 在 github 上创建发布
https://github.com/dimagi/commcare-export/releases
一旦发布发布,就会启动一个 GitHub 工作流,该工作流编译与 Linux 和 Windows 机器兼容的 DET 可执行文件,并将其作为资产添加到发布中。
【对于基于 Linux 的用户】如果您决定下载和使用可执行文件,请确保文件具有可执行权限,然后可以通过命令行像任何其他可执行文件一样调用它。
测试和测试数据库
以下命令将运行整个测试套件(需要设置如下 DB 环境变量)
$ py.test
要运行单个测试类或方法,您可以运行,例如
$ py.test -k "TestExcelQuery"
$ py.test -k "test_get_queries_from_excel"
要排除数据库测试,您可以运行
$ py.test -m "not dbtest"
在运行数据库测试时,支持 PostgreSQL、MySQL、MSSQL。
要对选定的数据库运行测试,可以使用以下标记:
$ py.test -m [postgres,mysql,mssql]
数据库URL可以通过环境变量进行覆盖。
POSTGRES_URL=postgresql://user:password@host/
MYSQL_URL=mysql+pymysql://user:password@host/
MSSQL_URL=mssql+pyodbc://user:password@host/
Postgresql
$ docker pull postgres:9.6
$ docker run --name ccexport-postgres -p 5432:5432 -e POSTGRES_PASSWORD=postgres -d postgres:9.6
$ export POSTGRES_URL=postgresql://postgres:postgres@localhost/
MySQL
$ docker pull mysql
$ docker run --name ccexport-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=pw -e MYSQL_USER=travis -e MYSQL_PASSWORD='' -d mysql
# create travis user
$ docker run -it --link ccexport-mysql:mysql --rm mysql sh -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p"$MYSQL_ENV_MYSQL_ROOT_PASSWORD"'
mysql> CREATE USER 'travis'@'%';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'travis'@'%';
MSSQL
$ docker pull mcr.microsoft.com/mssql/server:2017-latest
$ docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=Password@123" -p 1433:1433 --name mssql1 -d microsoft/mssql-server-linux:2017-latest
# install driver
$ curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
$ echo "deb [arch=amd64] https://packages.microsoft.com/ubuntu/$(lsb_release -rs)/prod $(lsb_release -rs) main" | sudo tee /etc/apt/sources.list.d/mssql-release.list
$ sudo apt-get update
$ sudo ACCEPT_EULA=Y apt-get install msodbcsql17
$ odbcinst -q -d
适用于Mac OS的MSSQL
$ docker pull mcr.microsoft.com/mssql/server:2017-latest
$ docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=Password@123" -p 1433:1433 --name mssql1 -d microsoft/mssql-server-linux:2017-latest
# Install driver
$ brew install unixodbc freetds
# Add the following 5 lines to /usr/local/etc/odbcinst.ini
[ODBC Driver 17 for SQL Server]
Description=FreeTDS Driver for Linux & MSSQL
Driver=/usr/local/lib/libtdsodbc.so
Setup=/usr/local/lib/libtdsodbc.so
UsageCount=1
# Create a soft link from /etc/odbcinst.ini to actual file
$ sudo ln -s /usr/local/etc/odbcinst.ini /etc/odbcinst.ini
集成测试
运行集成测试需要来自CommCare HQ的API凭证,该凭证可以访问corpora
域。此用户应仅具有访问corpora域的权限。
需要按以下方式设置环境变量:
$ export HQ_USERNAME=<username>
$ export HQ_API_KEY=<apikey>
对于Travis构建,这些作为加密变量包含在travis配置中。
项目详情
commcare-export-1.13.0.tar.gz的哈希
算法 | 哈希摘要 | |
---|---|---|
SHA256 | ca6316efe89ae837c5fa2d4c2e4e8756de08443d583c71045f22bbdaf0e32af7 |
|
MD5 | 3da20932ba3a2ad5eda019a139d76601 |
|
BLAKE2b-256 | 37ab08e670b046804050d49f1fbb326600c87db3f84106df77d9d49e8c020029 |