lifeomic / phc-sdk-py

The phc-sdk-py is a developer kit for interfacing with the PHC API on Python 3.8 and above.
https://lifeomic.github.io/phc-sdk-py/index.html
MIT License
1 stars 2 forks source link

Add simple recipe for survey results (Observation, QuestionnaireResponse, Questionnaire) #142

Open rcdilorenzo opened 3 years ago

rcdilorenzo commented 3 years ago

Right now, we don't have Questionnaire or QuestionnaireResponse. Each of these have to be merged with the appropriate Observation resources in order to have a nice frame of the results with the survey names and versions.

Example usage:

phc.SurveyObservation.get_data_frame() # => Returns observations that have the survey code

phc.Survey.get_data_frame() # => Returns surveys (questionnaires) and then you pick an ID
phc.SurveyObservation.get_data_frame(questionnaire_id="...", join_survey=True) # => (Allow multiple ids)

Here's some rough code of what's needed right now to accomplish this.

import pandas as pd

questionnaires_raw = phc.Query.execute_fhir_dsl({
    "type": "select",
    "columns": [
        {"expr": {"type": "column_ref", "column": c}}
        for c in ["id", "title", "meta", "version", "status", "date",
                  "subjectType", "identifier", "description", "contained"]
    ],
    "from": [{"table": "questionnaire"}],
}, page_size=100)

questionnaires = (
    phc.Frame
    .expand(pd.DataFrame([r["_source"] for r in questionnaires_raw]))
    .sort_values("version", ascending=False)
)

questionnaire_responses_raw = phc.Query.execute_fhir_dsl({
    "type": "select",
    "columns": [
        {"expr": {"type": "column_ref", "column": c}}
        for c in ["id", "status", "questionnaire"]
    ],
    "from": [{"table": "questionnaire_response"}],
}, all_results=True)

questionnaire_responses = phc.Frame.expand(
    pd.DataFrame([r["_source"] for r in questionnaire_responses_raw]),
    code_columns=["questionnaire"]
)

responses = phc.Observation.get_data_frame(
    code="...",
    system="http://lifeomic.com/fhir/primary-survey-id",
#     all_results=True
)

def join_observation_responses(
    observation_df: pd.DataFrame,
    questionnaire_response_df: pd.DataFrame,
    questionnaire_df: pd.DataFrame
):
    return observation_df.assign(**{
        "related.target_reference": observation_df["related.target_reference"].str.replace("QuestionnaireResponse/", "")
    }).join(
        questionnaire_response_df.set_index("id"),
        on="related.target_reference",
        rsuffix=".questionnaire_response"
    ).pipe(
        lambda df: df.assign(**{
            "questionnaire.reference": df["questionnaire.reference"].str.replace("Questionnaire/", "")
        })
    ).join(
        questionnaire_df.set_index("id"),
        on="questionnaire.reference",
        rsuffix=".questionnaire"
    )

join_observation_responses(responses, questionnaire_responses, questionnaires)[[
    "id",
    "subject.display",
    "status", # ?
    "title",
    "version",
    "status.questionnaire",
    "description",
    "status.questionnaire_response",
    "code.coding_system__lifeomic.com/fhir/questionnaire/item__code",
    "code.coding_system__lifeomic.com/fhir/questionnaire/item__display",
    "code.coding_system__lifeomic.com/fhir__code",
    "code.coding_system__lifeomic.com/fhir__display",
    "code.coding_system__lifeomic.com/fhir__userSelected",
    "valueCodeableConcept.coding_display",
    "valueCodeableConcept.coding_code",
    "valueString",
    "valueQuantity.value",
    "effectiveDateTime.local"
]].rename(columns={
    "code.coding_system__lifeomic.com/fhir/questionnaire/item__code": "item_code",
    "code.coding_system__lifeomic.com/fhir/questionnaire/item__display": "item", 
})
jairav commented 2 years ago

@alexccl I am going to take a crack at this with the assistance of Python lord @murrayln