Created
July 27, 2016 14:45
-
-
Save philomates/a6bc8e1913a3fad13e4439ead9eedfdf to your computer and use it in GitHub Desktop.
Script to update case properties on CommCareHQ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/python3 | |
""" | |
Script for posting a form to CommCareHQ that updates a case. | |
example usage: | |
python3 submit_case_update.py your-commcare-domain mobile-user1 passwd 123-this-is-a-case-id-456 prop1=new-value prop2="value with spaces" | |
""" | |
import datetime | |
import uuid | |
import sys | |
import requests | |
from requests.auth import HTTPBasicAuth | |
ISO_DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ' | |
SYSTEM_FORM_XMLNS = 'http://commcarehq.org/case' | |
SYSTEM_USER_ID = 'system' | |
BASE_URL = "https://www.commcarehq.org" | |
SUBMISSION_FILENAME = "submission.xml" | |
HELP_MESSAGE = """ | |
Script has 4 required arguments and unlimited optional case property arguments: | |
domain: the projects domain name on CommCareHQ | |
username: the mobile worker's username, used to submit the form | |
password: the mobile worker's password | |
case_id: id of case being updated | |
key=value: case property added or updated (there can be multiple of these) | |
""" | |
XML_TEMPLATE = """<?xml version='1.0' ?> | |
<system version="1" uiVersion="1" xmlns="{xmlns}" | |
xmlns:orx="http://openrosa.org/jr/xforms"> | |
<orx:meta xmlns:cc="http://commcarehq.org/xforms"> | |
<orx:deviceID /> | |
<orx:timeStart>{time}</orx:timeStart> | |
<orx:timeEnd>{time}</orx:timeEnd> | |
<orx:username>{username}</orx:username> | |
<orx:userID>{user_id}</orx:userID> | |
<orx:instanceID>{uid}</orx:instanceID> | |
<cc:appVersion /> | |
</orx:meta> | |
<case case_id="{case_id}" | |
date_modified="{date_modified}" | |
user_id="{user_id}" | |
xmlns="http://commcarehq.org/case/transaction/v2"> | |
<update> | |
{case_properties_block} | |
</update> | |
</case> | |
</system> | |
""" | |
# String String String String [Dict-of String String] -> None | |
def create_submission_file(filename, case_id, username, | |
user_id, case_properties): | |
submission_contents = format_update_form(case_id, username, | |
user_id, case_properties) | |
with open(filename, "w") as f: | |
f.write(submission_contents) | |
# String String String [Dict-of String String] -> String | |
def format_update_form(case_id, username, user_id, case_properties): | |
""" | |
Fill-in the case update form template to create a valid xml form | |
""" | |
now = datetime.datetime.utcnow().strftime(ISO_DATETIME_FORMAT) | |
form_id = uuid.uuid4().hex | |
context = { | |
'xmlns': SYSTEM_FORM_XMLNS, | |
'time': now, | |
'uid': form_id, | |
'username': username, | |
'user_id': user_id, | |
'case_id': case_id, | |
'date_modified': now, | |
'user_id': SYSTEM_USER_ID, | |
'case_properties_block': render_case_properties(case_properties) | |
} | |
return XML_TEMPLATE.format(**context) | |
# [Dict-of String String] -> String | |
def render_case_properties(case_properties): | |
props = "" | |
property_template = " <{key}>{value}</{key}>\n" | |
for key, value in case_properties.items(): | |
props += property_template.format(key=key, value=value) | |
return props | |
# same as | |
# curl --request POST -F [email protected] | |
# /a/your-domain/receiver -v -u | |
# [email protected]:password | |
def submit_form(filename, username, password, domain): | |
""" | |
Performs submission of xml file to CommCareHQ using basic auth | |
""" | |
file_data = {'xml_submission_file': | |
(filename, open(filename, 'rb'), 'multipart/form-data')} | |
url = "{0}/a/{1}/receiver/".format(BASE_URL, domain) | |
username = "{}@{}.commcarehq.org".format(username, domain) | |
r = requests.post( | |
url=url, | |
files=file_data, | |
auth=HTTPBasicAuth(username, password) | |
) | |
return r.status_code | |
# [List-of String] -> [Dict-of String String] | |
def parse_properties(args): | |
""" | |
Parses strings of form "key=value" into a dictionary | |
""" | |
props = {} | |
for prop in args: | |
index = prop.index("=") | |
if index > 0 and index + 1 < len(prop): | |
key = prop[:index] | |
value = prop[index+1:] | |
props[key] = value | |
else: | |
print("invalid case property '{}'".format(prop)) | |
sys.exit(1) | |
return props | |
def main(): | |
if len(sys.argv) < 5: | |
print(HELP_MESSAGE) | |
sys.exit(0) | |
domain = sys.argv[1] | |
username = sys.argv[2] | |
password = sys.argv[3] | |
case_id = sys.argv[4] | |
props = parse_properties(sys.argv[5:]) | |
filename = "submission.xml" | |
create_submission_file(filename, case_id, username, SYSTEM_USER_ID, props) | |
prop_key_str = ", ".join(props.keys()) | |
response_code = submit_form(filename, username, password, domain) | |
info_message = ("Updating {} as {}@{} with the following case properties" + | |
": {}").format(case_id, username, domain, prop_key_str) | |
if response_code < 200 or response_code > 299: | |
print("failed with HTTP code {}".format(response_code)) | |
sys.exit(1) | |
else: | |
print(info_message) | |
sys.exit(0) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment