Esempio n. 1
0
    def __init__(self, *args, **kwargs):
        """
        Constructor for the VisitAttribute class. This initializes the fields
        specific to the VisitAttribute class, and inherits from the Base class.

        Args:
            None
        """
        self.logger = logging.getLogger(self.__module__ + '.' +
                                        self.__class__.__name__)

        self.logger.addHandler(logging.NullHandler())

        # An instance of the DieseaseMeta class (composition).
        self._disease_meta = DiseaseMeta()

        # A flag to mark whether DiseaseMeta is dirty or not
        self._dm_dirty = False

        self._id = None
        self._tags = []
        self._links = {}
        self._version = None

        self._d = {"study": None, "tags": []}

        for propname, spec in VisitAttribute.__dict.iteritems():
            t = spec[0]
            x = property(VisitAttribute._bindRead(propname),
                         VisitAttribute._bindWrite(propname, t))
            setattr(self.__class__, propname, x)

        super(VisitAttribute, self).__init__(*args, **kwargs)
Esempio n. 2
0
    def testNciID(self):
        """ Test the nci_id property. """
        dis_meta = DiseaseMeta()

        self.util.stringTypeTest(self, dis_meta, "nci_id")

        self.util.stringPropertyTest(self, dis_meta, "nci_id")
Esempio n. 3
0
    def testName(self):
        """ Test the name property. """
        dis_meta = DiseaseMeta()

        self.util.stringTypeTest(self, dis_meta, "name")

        self.util.stringPropertyTest(self, dis_meta, "name")
Esempio n. 4
0
    def testMeshID(self):
        """ test the mesh_id method. """
        dis_meta = DiseaseMeta()

        self.util.stringTypeTest(self, dis_meta, "mesh_id")

        self.util.stringPropertyTest(self, dis_meta, "mesh_id")
Esempio n. 5
0
    def testDescription(self):
        """ Test the description property. """
        dis_meta = DiseaseMeta()

        self.util.stringTypeTest(self, dis_meta, "description")

        self.util.stringPropertyTest(self, dis_meta, "description")
Esempio n. 6
0
    def testComment(self):
        """ Test the comment property. """
        dis_meta = DiseaseMeta()

        self.util.stringTypeTest(self, dis_meta, "comment")

        self.util.stringPropertyTest(self, dis_meta, "comment")
Esempio n. 7
0
    def testToJson(self):
        dis_meta = DiseaseMeta()
        success = False

        dis_meta.name = "test name"
        dis_meta.description = "test description"
        dis_meta.comment = "test comment"
        dis_meta_json = None

        try:
            dis_meta_json = dis_meta.to_json()
            success = True
        except:
            pass

        self.assertTrue(success, "Able to use 'to_json'.")
        self.assertTrue(dis_meta_json is not None, "to_json() returned data.")
Esempio n. 8
0
    def testDiseaseOntologyID(self):
        dis_meta = DiseaseMeta()

        with self.assertRaises(ValueError):
            dis_meta.disease_ontology_id = True

        with self.assertRaises(ValueError):
            dis_meta.disease_ontology_id = 1

        with self.assertRaises(ValueError):
            dis_meta.disease_ontology_id = {}

        with self.assertRaises(ValueError):
            dis_meta.disease_ontology_id = []

        with self.assertRaises(ValueError):
            dis_meta.disease_ontology_id = 3.5

        disease_ontology_id = "test disease ontology ID"
        dis_meta.disease_ontology_id = disease_ontology_id

        self.assertEquals(disease_ontology_id, dis_meta.disease_ontology_id,
                          "disease_ontology_id property works.")
Esempio n. 9
0
    def testToJson(self):
        """ Test the generation of JSON from a DiseaseMeta instance. """
        dis_meta = DiseaseMeta()
        success = False

        dis_meta.name = "test name"
        dis_meta.description = "test description"
        dis_meta.comment = "test comment"
        dis_meta_json = None

        try:
            dis_meta_json = dis_meta.to_json()
            success = True
        except Exception:
            pass

        self.assertTrue(success, "Able to use 'to_json'.")
        self.assertTrue(dis_meta_json is not None, "to_json() returned data.")
Esempio n. 10
0
    def testComment(self):
        dis_meta = DiseaseMeta()

        with self.assertRaises(ValueError):
            dis_meta.comment = 1

        with self.assertRaises(ValueError):
            dis_meta.comment = {}

        with self.assertRaises(ValueError):
            dis_meta.comment = []

        with self.assertRaises(ValueError):
            dis_meta.comment = 3.5

        comment = "test comment"
        dis_meta.comment = comment

        self.assertEquals(comment, dis_meta.comment, "comment property works.")
Esempio n. 11
0
    def testDiseaseOntologyID(self):
        dis_meta = DiseaseMeta()

        with self.assertRaises(ValueError):
            dis_meta.disease_ontology_id = True

        with self.assertRaises(ValueError):
            dis_meta.disease_ontology_id = 1

        with self.assertRaises(ValueError):
            dis_meta.disease_ontology_id = {}

        with self.assertRaises(ValueError):
            dis_meta.disease_ontology_id = []

        with self.assertRaises(ValueError):
            dis_meta.disease_ontology_id = 3.5

        disease_ontology_id = "test disease ontology ID"
        dis_meta.disease_ontology_id = disease_ontology_id

        self.assertEquals(disease_ontology_id, dis_meta.disease_ontology_id,
                          "disease_ontology_id property works.")
Esempio n. 12
0
    def testDescription(self):
        dis_meta = DiseaseMeta()

        with self.assertRaises(ValueError):
            dis_meta.description = True

        with self.assertRaises(ValueError):
            dis_meta.description = 1

        with self.assertRaises(ValueError):
            dis_meta.description = {}

        with self.assertRaises(ValueError):
            dis_meta.description = []

        with self.assertRaises(ValueError):
            dis_meta.description = 3.5

        description = "test self condition"
        dis_meta.description = description

        self.assertEquals(description, dis_meta.description,
                          "description property works.")
Esempio n. 13
0
    def testName(self):
        dis_meta = DiseaseMeta()

        with self.assertRaises(ValueError):
            dis_meta.name = 30

        with self.assertRaises(ValueError):
            dis_meta.name = True

        with self.assertRaises(ValueError):
            dis_meta.name = {}

        with self.assertRaises(ValueError):
            dis_meta.name = []

        with self.assertRaises(ValueError):
            dis_meta.name = 3.5

        name = "test name"
        dis_meta.name = name

        self.assertEquals(name, dis_meta.name,
                          "name property works.")
Esempio n. 14
0
    def testNciID(self):
        dis_meta = DiseaseMeta()

        with self.assertRaises(ValueError):
            dis_meta.nci_id = True

        with self.assertRaises(ValueError):
            dis_meta.nci_id = 1

        with self.assertRaises(ValueError):
            dis_meta.nci_id = {}

        with self.assertRaises(ValueError):
            dis_meta.nci_id = []

        with self.assertRaises(ValueError):
            dis_meta.nci_id = 3.5

        nci_id = "test NCI ID"
        dis_meta.nci_id = nci_id

        self.assertEquals(nci_id, dis_meta.nci_id,
                          "nci_id property works.")
Esempio n. 15
0
    def testMeshID(self):
        dis_meta = DiseaseMeta()

        with self.assertRaises(ValueError):
            dis_meta.mesh_id = True

        with self.assertRaises(ValueError):
            dis_meta.mesh_id = 1

        with self.assertRaises(ValueError):
            dis_meta.mesh_id = {}

        with self.assertRaises(ValueError):
            dis_meta.mesh_id = []

        with self.assertRaises(ValueError):
            dis_meta.mesh_id = 3.5

        mesh_id = "test mesh ID"
        dis_meta.mesh_id = mesh_id

        self.assertEquals(mesh_id, dis_meta.mesh_id,
                          "mesh_id property works.")
Esempio n. 16
0
class VisitAttribute(Base):
    """
    The class encapsulating the data for an iHMP visit attribute.
    This class contains all the fields required to save a visit attribute in
    OSDF.

    Attributes:
        namespace (str): The namespace this class will use in OSDF.
    """
    namespace = "ihmp"

    __dict = {
        'comment': [str, None],
        'mother_child': [str, None],
        'study': [str, None],
        'survey_id': [str, None],
        'subproject': [str, None],
        'time_during_pregnancy': [str, None],

        # These are the disease metadata fields
        'disease_comment': ["DiseaseMeta.comment", None],
        'disease_name': ["DiseaseMeta.name", None],
        'disease_description': ["DiseaseMeta.description", None],
        'disease_ontology_id': ["DiseaseMeta.disease_ontology_id", None],
        'disease_mesh_id': ["DiseaseMeta.mesh_id", None],
        'disease_nci_id': ["DiseaseMeta.nci_id", None],
        'disease_umls_concept_id': ["DiseaseMeta.umls_concept_id", None],
        'disease_study_status': ["DiseaseMeta.study_disease_status", None],

        # pylint: disable=C0326
        # These are the clinical patient fields
        'age': [int, "clinical_patient"],
        'height': [float, "clinical_patient"],
        'weight': [float, "clinical_patient"],
        'weight_diff': [str, "clinical_patient"],
        'bmi': [float, "clinical_patient"],
        'hbi': [bool, "clinical_patient"],
        'hbi_total': [float, "clinical_patient"],
        'sccai': [bool, "clinical_patient"],
        'sccai_total': [float, "clinical_patient"],
        'fast_gluc': [int, "clinical_patient"],
        'thirtym_gluc': [int, "clinical_patient"],
        'sixtym_gluc': [int, "clinical_patient"],

        # These are the hrt fields
        'prior': [bool, "hrt"],
        'current': [bool, "hrt"],
        'duration': [str, "hrt"],

        # These are the health assessment fields
        'self_assess': [bool, "health_assessment"],
        'self_condition': [str, "health_assessment"],
        'abdominal_pain': [bool, "health_assessment"],
        'acute_dis': [str, "health_assessment"],
        'arthralgia': [bool, "health_assessment"],
        'bowel_day': [int, "health_assessment"],
        'bowel_night': [int, "health_assessment"],
        'cancer': [str, "health_assessment"],
        'cancer_mtc': [bool, "health_assessment"],
        'chest_pain': [bool, "health_assessment"],
        'claudication': [bool, "health_assessment"],
        'chronic_dis': [str, "health_assessment"],
        'diarrhea': [bool, "health_assessment"],
        'dyspnea': [bool, "health_assessment"],
        'ery_nodosum': [bool, "health_assessment"],
        'fever': [str, "health_assessment"],
        'leg_edema': [bool, "health_assessment"],
        'neurologic': [bool, "health_assessment"],
        'pregnant': [bool, "health_assessment"],
        'preg_plans': [bool, "health_assessment"],
        'pyo_gangrenosum': [bool, "health_assessment"],
        'rash': [bool, "health_assessment"],
        'stool_blood': [bool, "health_assessment"],
        'stool_soft': [int, "health_assessment"],
        'surgery': [str, "health_assessment"],
        'urgency_def': [str, "health_assessment"],
        'uveitis': [bool, "health_assessment"],
        'weight_change': [str, "health_assessment"],
        'diag_other': [str, "health_assessment"],
        'hosp': [bool, "health_assessment"],
        'work_missed': [int, "health_assessment"],

        # These are the medications fields
        'new_meds': [bool, "medications"],
        'stopped_meds': [bool, "medications"],
        'abx': [bool, "medications"],
        'chemo': [bool, "medications"],
        'immunosupp': [bool, "medications"],

        # These are the tests fields
        'colonoscopy': [bool, "tests"],
        'oral_contrast': [bool, "tests"],

        # These are the psych fields
        'psychiatric': [bool, "psych"],
        'upset': [int, "psych"],
        'control': [int, "psych"],
        'stress': [int, "psych"],
        'stress_def': [str, "psych"],
        'confident': [int, "psych"],
        'going_your_way': [int, "psych"],
        'coping': [int, "psych"],
        'irritation': [int, "psych"],
        'on_top': [int, "psych"],
        'anger': [int, "psych"],
        'difficulties': [int, "psych"],

        # These are the exercise fields
        'vig_activity_days': [int, "exercise"],
        'vig_activity_hours': [int, "exercise"],
        'vig_activity_minutes': [int, "exercise"],
        'mod_activity_days': [int, "exercise"],
        'mod_activity_hours': [int, "exercise"],
        'mod_activity_minutes': [int, "exercise"],
        'walking_days': [int, "exercise"],
        'walking_hours': [int, "exercise"],
        'walking_minutes': [int, "exercise"],
        'activity_30d': [str, "exercise"],
        'activity_3m': [str, "exercise"],
        'activity_change_30d': [str, "exercise"],
        'activity_change_3m': [str, "exercise"],

        # These are the dietary log fields
        'alcohol': [bool, "dietary_log"],
        'beans': [bool, "dietary_log"],
        'biscuit': [bool, "dietary_log"],
        'bread': [str, "dietary_log"],
        'bread_spread': [str, "dietary_log"],
        'breadrolls': [bool, "dietary_log"],
        'cheese': [bool, "dietary_log"],
        'cereal': [bool, "dietary_log"],
        'cereal_type': [str, "dietary_log"],
        'chips_crisps': [bool, "dietary_log"],
        'dairy': [bool, "dietary_log"],
        'diet_drinks': [bool, "dietary_log"],
        'eggs': [bool, "dietary_log"],
        'fish': [bool, "dietary_log"],
        'fish_white': [bool, "dietary_log"],
        'fish_oil': [bool, "dietary_log"],
        'fish_count': [int, "dietary_log"],
        'fruit': [bool, "dietary_log"],
        'fruit_count': [int, "dietary_log"],
        'grains': [bool, "dietary_log"],
        'ice_cream': [bool, "dietary_log"],
        'juice': [bool, "dietary_log"],
        'meat': [bool, "dietary_log"],
        'meat_red': [bool, "dietary_log"],
        'meat_white': [bool, "dietary_log"],
        'meat_product': [bool, "dietary_log"],
        'milk': [str, "dietary_log"],
        'pastry': [bool, "dietary_log"],
        'poultry': [bool, "dietary_log"],
        'probiotic': [bool, "dietary_log"],
        'salt': [str, "dietary_log"],
        'shellfish': [bool, "dietary_log"],
        'soda': [bool, "dietary_log"],
        'starch': [bool, "dietary_log"],
        'starch_type': [bool, "dietary_log"],
        'sugar': [str, "dietary_log"],
        'sugar_drinks': [bool, "dietary_log"],
        'sweets': [bool, "dietary_log"],
        'sweets_count': [int, "dietary_log"],
        'veg': [bool, "dietary_log"],
        'veg_green': [bool, "dietary_log"],
        'veg_root': [bool, "dietary_log"],
        'veg_raw': [bool, "dietary_log"],
        'water': [bool, "dietary_log"],
        'yogurt': [bool, "dietary_log"],

        # These are the dietary log "today" fields
        'breakfast_tod': [str, "dietary_log_today"],
        'breakfast_food': [str, "dietary_log_today"],
        'breakfast_amt': [str, "dietary_log_today"],
        'lunch_tod': [str, "dietary_log_today"],
        'lunch_food': [str, "dietary_log_today"],
        'lunch_amt': [str, "dietary_log_today"],
        'dinner_tod': [str, "dietary_log_today"],
        'dinner_food': [str, "dietary_log_today"],
        'dinner_amt': [str, "dietary_log_today"],
        'other_food_intake': [str, "dietary_log_today"]
    }

    @staticmethod
    # pylint: disable=W0211,W0613
    def _getx(self, propname, *args):
        if propname in self.__dict:
            propType = self.__dict[propname][0]
            if type(propType) == str and propType.startswith("DiseaseMeta."):
                dm_name = propType.replace("DiseaseMeta.", "", 1)
                value = getattr(self._disease_meta, dm_name)
            elif propname in self._d:
                value = self._d[propname]
            else:
                value = None
        else:
            raise AttributeError("Unknown attribute %s" % propname)

        return value

    @staticmethod
    # pylint: disable=W0211
    def _setx(self, value, n):
        self._d[n] = value

    @staticmethod
    def _bindRead(name):
        # pylint: disable=C0111
        def getXXXX(self, *args):
            return VisitAttribute._getx(self, name, *args)

        getXXXX.__name__ = name
        return getXXXX

    @staticmethod
    def _bindWrite(name, t):
        # pylint: disable=C0111
        def setXXXX(self, val):
            func = VisitAttribute._setx

            if t == str:
                func = enforce_string(func)
            elif t == int:
                func = enforce_int(func)
            elif t == float:
                func = enforce_float(func)
            elif t == list:
                func = enforce_list(func)
            elif t == bool:
                func = enforce_bool(func)
            elif t == dict:
                func = enforce_dict(func)

            func(self, val, name)

        setXXXX.__name__ = name
        return setXXXX

    def __init__(self, *args, **kwargs):
        """
        Constructor for the VisitAttribute class. This initializes the fields
        specific to the VisitAttribute class, and inherits from the Base class.

        Args:
            None
        """
        self.logger = logging.getLogger(self.__module__ + '.' +
                                        self.__class__.__name__)

        self.logger.addHandler(logging.NullHandler())

        # An instance of the DieseaseMeta class (composition).
        self._disease_meta = DiseaseMeta()

        # A flag to mark whether DiseaseMeta is dirty or not
        self._dm_dirty = False

        self._id = None
        self._tags = []
        self._links = {}
        self._version = None

        self._d = {"study": None, "tags": []}

        for propname, spec in VisitAttribute.__dict.iteritems():
            t = spec[0]
            x = property(VisitAttribute._bindRead(propname),
                         VisitAttribute._bindWrite(propname, t))
            setattr(self.__class__, propname, x)

        super(VisitAttribute, self).__init__(*args, **kwargs)

    def __setattr__(self, name, value):
        if name == "_d":
            self.__dict__[name] = value
            return

        if name not in VisitAttribute.__dict:
            super(VisitAttribute, self).__setattr__(name, value)
        else:
            nameType = self.__dict[name][0]
            if type(nameType) == str and nameType.startswith("DiseaseMeta."):
                dm_name = nameType.replace("DiseaseMeta.", "", 1)
                self.logger.debug("Setting %s %s property.", __name__, dm_name)
                setattr(self._disease_meta, dm_name, value)
                self.logger.debug("Setting flag that DiseaseMeta is dirty.")
                self._dm_dirty = True
            else:
                func = getattr(self.__class__, name)
                func.__set__(self, value)

    @staticmethod
    def required_fields():
        """
        A static method. The required fields for the class.

        Args:
            None
        Returns:
            Tuple of strings of required properties.
        """
        module_logger.debug("In required_fields.")

        return ("comment", "study", "tags")

    @staticmethod
    def load_visit_attr(attrib_data):
        """
        Takes the provided JSON string and converts it to a
        VisitAttribute object.

        Args:
            attrib_data (str): The JSON string to convert

        Returns:
            Returns a VisitAttribute instance.
        """
        module_logger.info("Creating a template %s.", __name__)
        attrib = VisitAttribute()

        module_logger.debug("Filling in %s details.", __name__)
        attrib._set_id(attrib_data['id'])
        attrib.links = attrib_data['linkage']
        attrib.version = attrib_data['ver']

        # Required fields
        attrib.comment = attrib_data['meta']['comment']
        attrib.study = attrib_data['meta']['study']
        attrib.survey_id = attrib_data['meta']['survey_id']
        attrib.tags = attrib_data['meta']['tags']

        # Handle optional fields
        attrib_metadata = attrib_data['meta']
        for (propname, spec) in VisitAttribute.__dict.iteritems():
            _cls = spec[0]
            section = spec[1]

            ## We need to handle any DiseaseMeta props separately here
            if not section:
                continue

            module_logger.debug("In section %s", section)

            # Handle any special cases that we need too.
            if (section == "excercise" or (propname.startswith('breakfast')
                                           or propname.startswith('lunch')
                                           or propname.startswith('dinner'))):
                (propbase, propkey) = propname.split('_', 1)

                propval = attrib_metadata.get(section, {}).get(propbase,
                                                               {}).get(propkey)
            elif propname == "sixtym_gluc":
                propval = attrib_metadata.get(section, {}).get('60m_gluc')
            elif propname == "thirtym_gluc":
                propval = attrib_metadata.get(section, {}).get('30m_gluc')
            else:
                propval = attrib_metadata.get(section, {}).get(propname)

            if propval:
                module_logger.debug("Setting prop %s to %s", propname, propval)
                setattr(attrib, propname, _cls(propval))

        # If any of the DiseaseMeta props exist we can handle them now
        if attrib_data['meta'].get('disease'):
            if attrib_data['meta']['disease'].get('study_disease_status'):
                attrib.disease_study_status = \
                    attrib_data['meta']['disease'].get('study_disease_status')

            disease_props = dict(('disease_%s' % key, value)
                                 for key, value in attrib_data['meta']
                                 ['disease']['study_disease'].iteritems())

            # This will have a double "disease" on it so we need to correct it.
            disease_props['disease_ontology_id'] = \
                disease_props.pop('disease_disease_ontology_id')

            map(lambda key: setattr(attrib, key, disease_props.get(key)),
                disease_props.keys())

        module_logger.debug("Returning loaded %s.", __name__)

        return attrib

    @staticmethod
    def load(attrib_id):
        """
        Loads the data for the node from OSDF to this object. If the provided
        ID does not exist, then an error message is generated.

        Args:
            attrib_id (str): The OSDF ID for the document to load.

        Returns:
            A VisitAttribute object with all the available OSDF data loaded
            into it.
        """
        module_logger.debug("In load. Specified ID: %s", attrib_id)

        session = iHMPSession.get_session()
        module_logger.info("Got iHMP session.")

        data = session.get_osdf().get_node(attrib_id)
        attrib = VisitAttribute.load_visit_attr(data)

        return attrib

    def validate(self):
        """
        Validates the current object's data against the schema in the OSDF instance.

        Args:
            None

        Returns:
            A list of strings, where each string is a validation error that the
            OSDF instance identified.
        """
        self.logger.debug("In validate.")

        document = self._get_raw_doc()

        session = iHMPSession.get_session()
        self.logger.info("Got iHMP session.")

        (valid, error_message) = session.get_osdf().validate_node(document)

        problems = []

        if not valid:
            self.logger.info("Validation did not succeed for %s.", __name__)
            problems.append(error_message)

        if 'associated_with' not in self._links.keys():
            problems.append("Must add an 'associated_with' link to a visit.")

        self.logger.debug("Number of validation problems: %s.", len(problems))

        return problems

    def is_valid(self):
        """
        Validates the current object's data/JSON against the current schema
        in the OSDF instance for the specific object. However, unlike
        validate(), this method does not provide exact error messages,
        it states if the validation was successful or not.

        Args:
            None

        Returns:
            True if the data validates, False if the current state of
            fields in the instance does not validate with OSDF.
        """
        self.logger.debug("In is_valid.")

        document = self._get_raw_doc()

        session = iHMPSession.get_session()
        self.logger.info("Got iHMP session.")

        (valid, _error_message) = session.get_osdf().validate_node(document)

        if 'associated_with' not in self._links.keys():
            self.logger.error("Must have an 'associated_with' linkage.")
            valid = False

        self.logger.debug("Valid? %s", str(valid))

        return valid

    def _get_raw_doc(self):
        self.logger.debug("In _get_raw_doc.")

        doc = {
            'acl': {
                'read': ['all'],
                'write': [VisitAttribute.namespace]
            },
            'linkage': self.links,
            'ns': VisitAttribute.namespace,
            'node_type': 'visit_attr',
            'meta': {
                'tags': self.tags,
                'comment': self.comment,
                'survey_id': self.survey_id,
                'study': self.study,
                'subtype': self.study
            }
        }

        # Go through each of the properties, and add it to the document
        # if it contains data
        for propname, spec in VisitAttribute.__dict.iteritems():
            # Don't encode 'special' properties that are delegated, such
            # as the DiseaseMeta fields...
            if spec[1] is None:
                continue

            value = getattr(self, propname)

            if value is not None:
                self.logger.debug("Value found for %s property.", propname)
                section = spec[1]
                # Set the section to a dictionary if it doesn't exist yet
                if section not in doc['meta']:
                    doc['meta'][section] = {}

                # Handle special cases
                if propname == "sixtym_gluc":
                    propname = "60m_gluc"
                elif propname == "thirtym_gluc":
                    propname = "30m_gluc"

                if propname == "vig_activity_days":
                    if "vig_activity" not in doc['meta']['exercise']:
                        doc['meta']['exercise']['vig_activity'] = {}
                    doc['meta']['exercise']['vig_activity']['days'] = value
                elif propname == "vig_activity_hours":
                    if "vig_activity" not in doc['meta']['exercise']:
                        doc['meta']['exercise']['vig_activity'] = {}
                    doc['meta']['exercise']['vig_activity']['hours'] = value
                elif propname == "vig_activity_minutes":
                    if "vig_activity" not in doc['meta']['exercise']:
                        doc['meta']['exercise']['vig_activity'] = {}
                    doc['meta']['exercise']['vig_activity']['minutes'] = value
                elif propname == "mod_activity_days":
                    if "mod_activity" not in doc['meta']['exercise']:
                        doc['meta']['exercise']['mod_activity'] = {}
                    doc['meta']['exercise']['mod_activity']['days'] = value
                elif propname == "mod_activity_hours":
                    if "mod_activity" not in doc['meta']['exercise']:
                        doc['meta']['exercise']['mod_activity'] = {}
                    doc['meta']['exercise']['mod_activity']['hours'] = value
                elif propname == "mod_activity_minutes":
                    if "mod_activity" not in doc['meta']['exercise']:
                        doc['meta']['exercise']['mod_activity'] = {}
                    doc['meta']['exercise']['mod_activity']['minutes'] = value
                elif propname == "walking_days":
                    if "walking" not in doc['meta']['exercise']:
                        doc['meta']['exercise']['walking'] = {}
                    doc['meta']['exercise']['walking']['days'] = value
                elif propname == "walking_hours":
                    if "walking" not in doc['meta']['exercise']:
                        doc['meta']['exercise']['walking'] = {}
                    doc['meta']['exercise']['walking']['hours'] = value
                elif propname == "walking_minutes":
                    if "walking" not in doc['meta']['exercise']:
                        doc['meta']['exercise']['walking'] = {}
                    doc['meta']['exercise']['walking']['minutes'] = value

                # dietary log "today"
                elif propname == "breakfast_tod":
                    if "breakfast" not in doc['meta']['dietary_log_today']:
                        doc['meta']['dietary_log_today']['breakfast'] = {}
                    doc['meta']['dietary_log_today']['breakfast'][
                        'tod'] = value
                elif propname == "breakfast_food":
                    if "breakfast" not in doc['meta']['dietary_log_today']:
                        doc['meta']['dietary_log_today']['breakfast'] = {}
                    doc['meta']['dietary_log_today']['breakfast'][
                        'food'] = value
                elif propname == "breakfast_amt":
                    if "breakfast" not in doc['meta']['dietary_log_today']:
                        doc['meta']['dietary_log_today']['breakfast'] = {}
                    doc['meta']['dietary_log_today']['breakfast'][
                        'amt'] = value

                elif propname == "lunch_tod":
                    if "lunch" not in doc['meta']['dietary_log_today']:
                        doc['meta']['dietary_log_today']['lunch'] = {}
                    doc['meta']['dietary_log_today']['lunch']['tod'] = value
                elif propname == "lunch_food":
                    if "lunch" not in doc['meta']['dietary_log_today']:
                        doc['meta']['dietary_log_today']['lunch'] = {}
                    doc['meta']['dietary_log_today']['lunch']['food'] = value
                elif propname == "lunch_amt":
                    if "lunch" not in doc['meta']['dietary_log_today']:
                        doc['meta']['dietary_log_today']['lunch'] = {}
                    doc['meta']['dietary_log_today']['lunch']['amt'] = value

                elif propname == "dinner_tod":
                    if "dinner" not in doc['meta']['dietary_log_today']:
                        doc['meta']['dietary_log_today']['dinner'] = {}
                    doc['meta']['dietary_log_today']['dinner']['tod'] = value
                elif propname == "dinner_food":
                    if "dinner" not in doc['meta']['dietary_log_today']:
                        doc['meta']['dietary_log_today']['dinner'] = {}
                    doc['meta']['dietary_log_today']['dinner']['food'] = value
                elif propname == "dinner_amt":
                    if "dinner" not in doc['meta']['dietary_log_today']:
                        doc['meta']['dietary_log_today']['dinner'] = {}
                    doc['meta']['dietary_log_today']['dinner']['amt'] = value

                else:
                    doc['meta'][section][propname] = value

        # If we've configured fields in the DiseaseMeta class, fill the disease
        # portion of the document, which is delegated to the DiseaseMeta class.
        if self._dm_dirty:
            doc['meta']['disease'] = self._disease_meta._get_raw_doc()

        if self._id is not None:
            self.logger.debug("%s object has the OSDF id set.", __name__)
            doc['id'] = self._id

        if self._version is not None:
            self.logger.debug("%s object has the OSDF version set.", __name__)
            doc['ver'] = self._version

        return doc

    @staticmethod
    def search(query="\"visit_attr\"[node_type]"):
        """
        Searches OSDF for VisitAttribute nodes. Any criteria the user wishes to
        add is provided by the user in the query language specifications
        provided in the OSDF documentation. A general format is (including the
        quotes and brackets):

        "search criteria"[field to search]

        If there are any results, they are returned as SampleAttribute instances,
        otherwise an empty list will be returned.

        Args:
            query (str): The query for the OSDF framework. Defaults to the
                         SampleAttribute node type.

        Returns:
            Returns an array of VisitAttribute objects. It returns an empty
            list if there are no results.
        """
        module_logger.debug("In search.")

        session = iHMPSession.get_session()
        module_logger.info("Got iHMP session.")

        if query != '"visit_attr"[node_type]':
            query = '({}) && "visit_attr"[node_type]'.format(query)

        module_logger.debug("Submitting OQL query: %s", query)

        attrib_data = session.get_osdf().oql_query(VisitAttribute.namespace,
                                                   query)

        all_results = attrib_data['results']

        result_list = list()

        if len(all_results) > 0:
            for result in all_results:
                attrib_result = VisitAttribute.load_visit_attr(result)
                result_list.append(attrib_result)

        return result_list

    def save(self):
        """
        Saves the data to OSDF. The JSON form of the object is not valid, then
        the data is not saved. If the instance was saved previously, then the
        node ID is assigned the alphanumeric assigned by the OSDF instance. If
        not saved previously, then the node ID is 'None', and upon a successful
        save, will be defined as the alphanumeric ID from OSDF.  In addition,
        the document's version is updated when a successful save operation is
        completed.

        Args:
            None

        Returns;
            True if successful, False otherwise.

        """
        self.logger.debug("In save.")

        if not self.is_valid():
            self.logger.error("Cannot save, data is invalid.")
            return False

        session = iHMPSession.get_session()
        self.logger.info("Got iHMP session.")

        success = False

        if self._id is None:
            # The document has not yet been saved
            data = self._get_raw_doc()
            self.logger.info("Got the raw JSON document.")

            try:
                self.logger.info("Attempting to save a new %snode.", __name__)
                node_id = session.get_osdf().insert_node(data)
                self.logger.info("Save for %s %s successful.", __name__,
                                 node_id)
                self.logger.info("Setting ID for %s %s.", __name__, node_id)
                self._set_id(node_id)
                self._version = 1
                success = True
            except Exception as save_exception:
                self.logger.error("An error occurred while saving %s." + \
                                  "Reason: %s", __name__, save_exception)
        else:
            data = self._get_raw_doc()
            try:
                self.logger.info("Attempting to update ID: %s.", self.id)
                session.get_osdf().edit_node(data)
                self.logger.info("Update for %s successful.", self.id)
                success = True
            except Exception as edit_exception:
                msg = "An error occurred while updating %s %s. Reason: %s" \
                       % (__name__, self.id, edit_exception)
                self.logger.error(msg)

        return success