Ejemplo n.º 1
0
    def __init__(self, registry_model, patient_model, questionnaire):
        self.registry_model = registry_model
        self.humaniser = Humaniser(self.registry_model)
        self.patient_model = patient_model
        self.patient_data = self.patient_model.get_dynamic_data(
            self.registry_model)

        self.questionnaire = questionnaire
        self.gfe_parser = GeneralisedFieldExpressionParser(self.registry_model)
        self.default_context_model = patient_model.default_context(
            registry_model)
        self.name = "%s" % self.patient_model
Ejemplo n.º 2
0
    def __init__(self, query_model, humaniser):
        self.query_model = query_model
        self.humaniser = humaniser
        self.registry_model = query_model.registry
        self.projection_list = json.loads(query_model.projection)
        self.longitudinal_column_map = self._build_longitudinal_column_map()
        self.work_book = xl.Workbook()
        self.current_sheet = None
        self.current_row = 1
        self.current_col = 1
        self.time_window = default_time_window()
        self.patient_fields = self._get_patient_fields()
        self.cde_model_map = {}
        self.cache = Cache()
        self._universal_column_map = {}

        self.gfe_func_map = {}
        self.parser = GeneralisedFieldExpressionParser(self.registry_model)
Ejemplo n.º 3
0
class _ExistingDataWrapper(object):
    """
    return to qr view to show data already saved on a patient
    """

    def __init__(self, registry_model, patient_model, questionnaire):
        self.registry_model = registry_model
        self.humaniser = Humaniser(self.registry_model)
        self.patient_model = patient_model
        self.patient_data = self.patient_model.get_dynamic_data(
            self.registry_model)

        self.questionnaire = questionnaire
        self.gfe_parser = GeneralisedFieldExpressionParser(self.registry_model)
        self.default_context_model = patient_model.default_context(
            registry_model)
        self.name = "%s" % self.patient_model

    def _get_field_data(self, field_expression, form_model, section_model, cde_model):
        if field_expression in list(KEY_MAP.keys()):
            field_expression = KEY_MAP[field_expression][
                0]  # the demographic field

        retrieval_function = self.gfe_parser.parse(field_expression)
        try:
            value = retrieval_function(self.patient_model, self.patient_data)
            if field_expression == "working_groups":
                return self._get_working_groups_display_value(value)
            value = self.humaniser.display_value2(
                form_model, section_model, cde_model, value)

            if isinstance(value, datetime) or isinstance(value, date):
                value = str(value)
            return value

        except Exception as ex:
            return "Error[!%s]" % ex

    def _get_working_groups_display_value(self, working_group_models):
        return ",".join(sorted([wg.name for wg in working_group_models]))

    @property
    def link(self):
        demographic_link = reverse("patient_edit", args=[self.registry_model.code,
                                                         self.patient_model.pk])
        return demographic_link

    @property
    def questions(self):
        """
        If data already filled in for patient
        grab and return as list of wrappers for view
        There are two special sections PatientData and PatientAddressData
        """
        lst = []
        for question in self.questionnaire.questions:
            if isinstance(question, _ConsentQuestion):
                existing_answer = {"name": question.name,
                                   "pos": str(question.pos),
                                   "is_multi": False,
                                   "answer": self._get_consent_answer(question)
                                   }
                lst.append(existing_answer)
                continue

            if question.section_code == "PatientData":
                field_name = self._get_patient_data_field_name(
                    question.cde_code)
                field_expression = question.cde_code
            elif question.section_code == 'PatientAddressSection':
                field_name = self._get_patient_address_field_name(
                    question.cde_code)
                field_expression = "address field expression"
            else:
                try:
                    field_name = question.target.display_name
                    field_expression = question.target.field_expression
                except Exception as ex:
                    logger.error("could not get target for %s %s: %s" % (question.section_code,
                                                                         question.cde_code,
                                                                         ex))
                    continue

            if field_name in KEY_MAP:
                field_name = question.cde_model.name

            if not question.is_multi:
                existing_answer = {
                    "name": field_name,
                    "pos": str(
                        question.pos),
                    "is_multi": False,
                    "answer": self._get_field_data(
                        field_expression,
                        question.form_model,
                        question.section_model,
                        question.cde_model)}
            else:
                if not question.is_address:
                    existing_answer = {
                        "name": field_name,
                        "pos": str(
                            question.pos),
                        "is_multi": True,
                        "answers": self._get_existing_multisection_data(
                            question.field_expression,
                            question.form_model,
                            question.section_model)}
                else:
                    existing_answer = {
                        "name": field_name, "pos": str(
                            question.pos), "is_multi": True, "answers": self._get_address_labels(
                            question.field_expression)}

            lst.append(existing_answer)

        return lst

    def _get_consent_answer(self, consent_question):
        from registry.patients.models import ConsentValue
        try:
            consent_value_model = ConsentValue.objects.get(
                patient=self.patient_model,
                consent_question=consent_question.consent_question_model)
            if consent_value_model.answer:
                return "Yes"
        except ConsentValue.DoesNotExist:
            return "No"

        return "No"

    def _get_address_labels(self, addresses_expression):

        def address_label(address):
            try:
                atype = address.address_type.description
            except Exception as ex:
                atype = "%s" % ex

            return "%s: %s %s %s %s %s" % (atype,
                                           address.address,
                                           address.suburb,
                                           address.state,
                                           address.postcode,
                                           address.country)

        retriever = self.gfe_parser.parse(addresses_expression)
        address_objects = retriever.evaluate(
            self.patient_model, self.patient_data)
        return [address_label(address) for address in address_objects]

    def _get_existing_multisection_data(self, field_expression, form_model, section_model):
        items_retriever = self.gfe_parser.parse(field_expression)
        display_items = []
        raw_items = items_retriever(self.patient_model, self.patient_data)

        for item in raw_items:
            display_fields = []
            for cde_code in item:
                cde_model = CommonDataElement.objects.get(code=cde_code)
                display_name = cde_model.name
                raw_value = item[cde_code]
                display_value = self.humaniser.display_value2(form_model,
                                                              section_model,
                                                              cde_model,
                                                              raw_value)
                display_field = "%s=%s" % (display_name, display_value)
                display_fields.append(display_field)

            item_field = ",".join(display_fields)
            display_items.append(item_field)

        # list of items displayed as key value pairs
        return display_items

    def _get_patient_data_field_name(self, cde_code):
        return cde_code

    def _get_patient_address_field_name(self, cde_code):
        return cde_code
Ejemplo n.º 4
0
class SpreadSheetReport:
    def __init__(self, query_model, humaniser):
        self.query_model = query_model
        self.humaniser = humaniser
        self.registry_model = query_model.registry
        self.projection_list = json.loads(query_model.projection)
        self.longitudinal_column_map = self._build_longitudinal_column_map()
        self.work_book = xl.Workbook()
        self.current_sheet = None
        self.current_row = 1
        self.current_col = 1
        self.time_window = default_time_window()
        self.patient_fields = self._get_patient_fields()
        self.cde_model_map = {}
        self.cache = Cache()
        self._universal_column_map = {}

        self.gfe_func_map = {}
        self.parser = GeneralisedFieldExpressionParser(self.registry_model)

    # Public interface
    def run(self, output_filename):
        self._generate()
        self.work_book.save(output_filename)

    # Private

    def _add_cde_models(self, cde_codes):
        for cde_code in cde_codes:
            if cde_code not in self.cde_model_map:
                self.cde_model_map[cde_code] = CommonDataElement.objects.get(
                    code=cde_code)

    def _get_patient_fields(self):
        from registry.patients.models import Patient
        patient_fields = set(
            [field.name for field in Patient._meta.get_fields()])
        return patient_fields

    def _build_longitudinal_column_map(self):
        d = {}
        for cde_dict in self.projection_list:
            if cde_dict["longitudinal"]:
                form_name = cde_dict["formName"]
                section_code = cde_dict["sectionCode"]
                if (form_name, section_code) in d:
                    d[(form_name, section_code)].append(cde_dict["cdeCode"])
                else:
                    d[(form_name, section_code)] = [cde_dict["cdeCode"]]
        return d

    # Movement helper functions

    def _next_cell(self):
        self.current_col += 1

    def _next_row(self):
        self.current_row += 1
        self.current_col = 1

    def _reset(self):
        self.current_col = 1
        self.current_row = 1

    # Writing

    def _write_cell(self, value):
        # write value at current position
        cell = self.current_sheet.cell(row=self.current_row,
                                       column=self.current_col)
        try:
            cell.value = value
        except Exception as ex:
            logger.error("error writing value %s to cell: %s" % (value, ex))
            cell.value = "?ERROR?"
        self._next_cell()

    def _generate(self):
        config = json.loads(self.query_model.sql_query)
        static_sheets = config["static_sheets"]
        for static_sheet_dict in static_sheets:
            name = self.registry_model.code.upper() + static_sheet_dict["name"]
            columns = static_sheet_dict["columns"]
            self._create_static_sheet(name, columns)
        # these columns are always included prior
        universal_columns = config["universal_columns"]
        # to the longitudinal data
        for form_model in self.registry_model.forms:
            for section_model in form_model.section_models:
                if self._section_in_report(form_model, section_model):
                    self._create_longitudinal_section_sheet(
                        universal_columns, form_model, section_model)

    def _section_in_report(self, form_model, section_model):
        for cde_dict in self.projection_list:
            if form_model.name == cde_dict[
                    "formName"] and section_model.code == cde_dict[
                        "sectionCode"]:
                return True

    def _create_static_sheet(self, name, columns):
        self._create_sheet(name)
        self._write_header_row(columns)
        self._next_row()

        for patient in self._get_patients():
            patient_record = self.cache.get_current(patient,
                                                    self._get_patient_record)
            self._write_row(patient, patient_record, columns)
            self._next_row()

    def _write_row(self, patient, patient_record, columns):
        for column in columns:
            value_retriever = self._get_value_retriever(column)
            value = value_retriever(patient, patient_record)
            self._write_cell(value)

    def _get_timestamp_from_snapshot(self, snapshot):
        if "timestamp" in snapshot:
            return snapshot["timestamp"]

    def _get_cde_value_from_snapshot(self, snapshot, form_model, section_model,
                                     cde_model):
        patient_record = snapshot["record"]
        if patient_record is None:
            return ""
        try:
            return self._human(
                form_model, section_model, cde_model,
                get_cde_value(form_model, section_model, cde_model,
                              patient_record))
        except Exception as ex:
            patient_id = patient_record["django_id"]
            logger.error(
                "Error getting cde %s/%s/%s for patient %s snapshot: %s" %
                (form_model.name, section_model.code, cde_model.code,
                 patient_id, ex))

            return "?ERROR?"

    @cached
    def _human(self, form_model, section_model, cde_model, raw_cde_value):
        if not isinstance(raw_cde_value, type([])):
            return self.humaniser.display_value2(form_model, section_model,
                                                 cde_model, raw_cde_value)
        else:
            return ",".join([
                str(
                    self.humaniser.display_value2(form_model, section_model,
                                                  cde_model, x))
                for x in raw_cde_value
            ])

    def _get_value_retriever(self, column):
        if column in self.gfe_func_map:
            return self.gfe_func_map[column]
        else:
            value_retriever = self.parser.parse(column)
            self.gfe_func_map[column] = value_retriever
            return value_retriever

    def _write_universal_columns(self, patient, patient_record,
                                 universal_columns):
        patient_id = patient.pk
        if patient_id in self._universal_column_map:
            for column in universal_columns:
                self._write_cell(
                    self._universal_column_map[patient_id][column])
        else:
            self._universal_column_map[patient_id] = {}
            for column in universal_columns:
                value_retriever = self._get_value_retriever(column)
                value = value_retriever(patient, patient_record)
                self._write_cell(value)
                self._universal_column_map[patient_id][column] = value

    def _create_longitudinal_section_sheet(self, universal_columns, form_model,
                                           section_model):
        sheet_name = self.registry_model.code.upper() + section_model.code
        sheet_name = sheet_name[:30]  # 31 max char sheet name size ...
        self._create_sheet(sheet_name)
        # this just writes out bit at the beginning
        self._write_header_universal_columns(universal_columns)
        first_longitudinal_date_col = self.current_col
        self._next_row()
        max_snapshots = 0
        cde_codes = self.longitudinal_column_map.get(
            (form_model.name, section_model.code), [])

        self._add_cde_models(cde_codes)

        for patient in self._get_patients():
            patient_record = self.cache.get_current(patient,
                                                    self._get_patient_record)
            self._write_universal_columns(patient, patient_record,
                                          universal_columns)
            num_snapshots = self._write_longitudinal_row(
                patient, patient_record, form_model, section_model, cde_codes)
            if num_snapshots > max_snapshots:
                max_snapshots = num_snapshots
            self._next_row()

        # write longitudinal header row now we know max width
        self.current_col = first_longitudinal_date_col
        self.current_row = 1
        column_prefix = "%s/%s" % (form_model.name.upper(), section_model.code)
        date_column_name = "DATE_%s" % column_prefix
        if max_snapshots == 0:
            self._write_cell(date_column_name)
            for cde_code in cde_codes:
                self._write_cell(cde_code)
            self._write_cell("NO DATA RECORDED!")

        else:
            for i in range(max_snapshots):
                self._write_cell(date_column_name)
                for cde_code in cde_codes:
                    self._write_cell(cde_code)

    def _create_sheet(self, title):
        sheet = self.work_book.create_sheet()
        sheet.title = title
        self.current_sheet = sheet
        self._reset()

    def _write_header_row(self, columns):
        for column_name in columns:
            self._write_cell(self._header_name(column_name))

    def _header_name(self, column_name):
        if column_name.startswith("@"):
            return column_name[1:]
        else:
            return column_name

    def _get_patients(self):
        from registry.patients.models import Patient
        return Patient.objects.filter(
            rdrf_registry__in=[self.registry_model]).order_by("id")

    def _get_patient_record(self, patient, collection="cdes"):
        wrapper = DynamicDataWrapper(patient)
        return wrapper.load_dynamic_data(self.registry_model.code,
                                         collection,
                                         flattened=False)

    def _write_header_universal_columns(self, universal_columns):
        for column_name in universal_columns:
            self._write_cell(self._header_name(column_name))

    def _write_longitudinal_row(self, patient, patient_record, form_model,
                                section_model, cde_codes):
        num_blocks = 0  # a "block" is all cdes in one snapshot or the current set of cdes
        # we will write the current set of cdes last

        # for snapshot in self._get_snapshots(patient):
        for snapshot in self.cache.get_snapshots(patient, self._get_snapshots):
            num_blocks += 1
            timestamp = self._get_timestamp_from_snapshot(snapshot)
            values = []
            for cde_code in cde_codes:
                cde_model = self.cde_model_map[cde_code]
                value = self._get_cde_value_from_snapshot(
                    snapshot, form_model, section_model, cde_model)
                values.append(value)
            self._write_cell(timestamp)
            for value in values:
                self._write_cell(value)
        # now write the current block
        current_timestamp = self._get_timestamp_from_current_form_data(
            patient_record, form_model)
        self._write_cell(current_timestamp)
        # TODO fix me The following is incomplete?
        # for cde_code in cde_codes:
        #     cde_model = self.cde_model_map[cde_code]
        #     current_value = self._human(
        #         form_model,
        #         section_model,
        #         cde_model,
        #         self._get_cde_value_from_current_data(
        #             patient_record,
        #             form_model,
        #             section_model,
        #             cde_model))

        #                    snap1        snap2      current
        # E.g 3 blocks  = [date A B C][date A B C][date A B C] - we return
        # number so we can write the header ...
        return num_blocks

    def _get_timestamp_from_current_form_data(self, patient_record,
                                              form_model):
        if patient_record is None:
            return None
        else:
            for form_dict in patient_record["forms"]:
                if form_dict["name"] == form_model.name:
                    if "timestamp" in form_dict:
                        return form_dict["timestamp"]

    def _get_cde_value_from_current_data(self, patient_record, form_model,
                                         section_model, cde_model):
        if patient_record is None:
            return None
        try:
            return get_cde_value(form_model, section_model, cde_model,
                                 patient_record)
        except Exception as ex:
            cde = "%s/%s/%s" % (form_model.name, section_model.code,
                                cde_model.code)
            logger.error("Error getting current cde %s for %s: %s" %
                         (cde, patient_record["django_id"], ex))
            return "??ERROR??"

    def _get_snapshots(self, patient):
        history = ClinicalData.objects.collection(self.registry_model.code,
                                                  "history")
        snapshots = history.find(patient, record_type="snapshot")

        # # fixme: date filtering was never implemented, but it could
        # # be added if the storage format of history timestamps is fixed.
        # if self.time_window:
        #     after, before = self.time_window
        #     if after is not None:
        #         snapshots = snapshots.filter(data__timestamp__gte=after.isoformat())
        #     if before is not None:
        #         snapshots = snapshots.filter(data__timestamp__lte=before.isoformat())

        return list(snapshots.data())