Skip to content

Commit

Permalink
commit espo_routes & kobo_utils
Browse files Browse the repository at this point in the history
  • Loading branch information
tijsziere committed Sep 27, 2024
1 parent b668530 commit 17b4de0
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 9 deletions.
204 changes: 197 additions & 7 deletions routes/espo_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,206 @@

router = APIRouter()

@router.post("/kobo-to-espocrm")
@app.post("/kobo-to-espocrm")
async def kobo_to_espocrm(
request: Request, dependencies=Depends(required_headers_espocrm)
):
"""Send a Kobo submission to EspoCRM."""
# ... existing code ...

kobo_data = await request.json()
extra_logs = {"environment": os.getenv("ENV")}
try:
extra_logs["kobo_form_id"] = str(kobo_data["_xform_id_string"])
extra_logs["kobo_form_version"] = str(kobo_data["__version__"])
extra_logs["kobo_submission_id"] = str(kobo_data["_id"])
except KeyError:
return JSONResponse(
status_code=422,
content={"detail": "Not a valid Kobo submission"},
)
target_response = {}

# store the submission uuid and status, to avoid duplicate submissions
submission = add_submission(kobo_data)
if submission["status"] == "success":
logger.info(
"Submission has already been successfully processed", extra=extra_logs
)
return JSONResponse(
status_code=200,
content={"detail": "Submission has already been successfully processed"},
)

kobo_data = clean_kobo_data(kobo_data)

# Check if 'skipConnect' is present and set to True in kobo_data
if "skipconnect" in kobo_data.keys() and kobo_data["skipconnect"] == "1":
logger.info("Skipping submission", extra=extra_logs)
return JSONResponse(status_code=200, content={"message": "Skipping submission"})

kobotoken, koboasset = None, None
if "kobotoken" in request.headers.keys():
kobotoken = request.headers["kobotoken"]
if "koboasset" in request.headers.keys():
koboasset = request.headers["koboasset"]
client = EspoAPI(request.headers["targeturl"], request.headers["targetkey"])
attachments = get_attachment_dict(kobo_data, kobotoken, koboasset)

# check if records need to be updated
update_record_payload = {}
if "updaterecordby" in request.headers.keys():
if "updaterecordby" in kobo_data.keys():
if (
kobo_data["updaterecordby"] != ""
and kobo_data["updaterecordby"] is not None
):
update_record_entity = request.headers["updaterecordby"].split(".")[0]
update_record_field = request.headers["updaterecordby"].split(".")[1]
update_record_payload[update_record_entity] = {
"field": update_record_field,
"value": kobo_data["updaterecordby"],
}
kobo_data.pop("updaterecordby")

# Create API payload body
payload, target_entity = {}, ""
for kobo_field, target_field in request.headers.items():

kobo_value, multi, repeat, repeat_no, repeat_question = "", False, False, 0, ""

# determine if kobo_field is of type multi or repeat
if "multi." in kobo_field:
kobo_field = kobo_field.split(".")[1]
multi = True
if "repeat." in kobo_field:
split = kobo_field.split(".")
kobo_field = split[1]
repeat_no = int(split[2])
repeat_question = split[3]
repeat = True

# check if kobo_field is in kobo_data
if kobo_field not in kobo_data.keys():
continue

# check if entity is nested in target_field
if len(target_field.split(".")) == 2:
target_entity = target_field.split(".")[0]
target_field = target_field.split(".")[1]
if target_entity not in payload.keys():
payload[target_entity] = {}
else:
continue

# get kobo_value based on kobo_field type
if multi:
kobo_value = kobo_data[kobo_field].split(" ")
elif repeat:
if 0 <= repeat_no < len(kobo_data[kobo_field]):
kobo_data[kobo_field][repeat_no] = clean_kobo_data(
kobo_data[kobo_field][repeat_no]
)
if repeat_question not in kobo_data[kobo_field][repeat_no].keys():
continue
kobo_value = kobo_data[kobo_field][repeat_no][repeat_question]
else:
continue
else:
kobo_value = kobo_data[kobo_field]

# process individual field; if it's an attachment, upload it to EspoCRM
kobo_value_url = str(kobo_value).replace(" ", "_")
kobo_value_url = re.sub(r"[(,)']", "", kobo_value_url)
if kobo_value_url not in attachments.keys():
payload[target_entity][target_field] = kobo_value
else:
file_url = attachments[kobo_value_url]["url"]
if not kobotoken:
error_message = "'kobotoken' needs to be specified in headers to upload attachments to EspoCRM"
logger.error(f"Failed: {error_message}", extra=extra_logs)
update_submission_status(submission, "failed", error_message)

# encode attachment in base64
file = get_kobo_attachment(file_url, kobotoken)
file_b64 = base64.b64encode(file).decode("utf8")
# upload attachment to target
attachment_payload = {
"name": kobo_value,
"type": attachments[kobo_value_url]["mimetype"],
"role": "Attachment",
"relatedType": target_entity,
"field": target_field,
"file": f"data:{attachments[kobo_value_url]['mimetype']};base64,{file_b64}",
}
attachment_record = espo_request(
submission,
client,
"POST",
"Attachment",
params=attachment_payload,
logs=extra_logs,
)
# link field to attachment
payload[target_entity][f"{target_field}Id"] = attachment_record["id"]

if len(payload) == 0:
error_message = "No fields found in submission or no entities found in headers"
logger.error(f"Failed: {error_message}", extra=extra_logs)
update_submission_status(submission, "failed", error_message)

for target_entity in payload.keys():

if target_entity not in update_record_payload.keys():
# create new record of target entity
response = espo_request(
submission,
client,
"POST",
target_entity,
params=payload[target_entity],
logs=extra_logs,
)
else:
# find target record
params = {
"where": [
{
"type": "contains",
"attribute": update_record_payload[target_entity]["field"],
"value": update_record_payload[target_entity]["value"],
}
]
}
records = espo_request(
submission, client, "GET", target_entity, params=params, logs=extra_logs
)["list"]
if len(records) != 1:
error_message = (
f"Found {len(records)} records of entity {target_entity} "
f"with field {update_record_payload[target_entity]['field']} "
f"equal to {update_record_payload[target_entity]['value']}"
)
logger.error(f"Failed: {error_message}", extra=extra_logs)
update_submission_status(submission, "failed", error_message)
response = {}
else:
# update target record
response = espo_request(
submission,
client,
"PUT",
f"{target_entity}/{records[0]['id']}",
params=payload[target_entity],
logs=extra_logs,
)
if "id" not in response.keys():
error_message = response.content.decode("utf-8")
logger.error(f"Failed: {error_message}", extra=extra_logs)
update_submission_status(submission, "failed", error_message)
else:
target_response[target_entity] = response

logger.info("Success", extra=extra_logs)
update_submission_status(submission, "success")
return JSONResponse(status_code=200, content=target_response)

@router.post("/kobo-to-121")
async def kobo_to_121(request: Request, dependencies=Depends(required_headers_121)):
"""Send a Kobo submission to 121."""
# ... existing code ...
return JSONResponse(status_code=import_response.status_code, content=import_response_message)
11 changes: 9 additions & 2 deletions utils/kobo_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
from datetime import datetime, timedelta
from azure.cosmos.exceptions import CosmosResourceExistsError

# Define utility functions here
def required_headers_kobo(kobotoken: str = Header(), koboasset: str = Header()):
return kobotoken, koboasset

def required_headers_121_kobo(
url121: str = Header(), username121: str = Header(), password121: str = Header(), kobotoken: str = Header(), koboasset: str = Header()
):
return url121, username121, password121, kobotoken, koboasset

def add_submission(kobo_data):
"""Add submission to CosmosDB. If submission already exists and status is pending, raise HTTPException."""
Expand Down Expand Up @@ -89,4 +95,5 @@ def clean_kobo_data(kobo_data):
for key in list(kobo_data_clean.keys()):
new_key = key.split("/")[-1]
kobo_data_clean[new_key] = kobo_data_clean.pop(key)
return kobo_data_clean
return kobo_data_clean

0 comments on commit 17b4de0

Please sign in to comment.