Repository: NREL/hescore-hpxml

Issue:
Workaround for conditioned attics in HPXML 2.3
There is [no way to translate a conditioned attic](https://hescore-hpxml.readthedocs.io/en/latest/translation/zone_roof.html#id1) from HPXML 2.3. It [will be possible](https://hpxml.nrel.gov/datadictionary/3/Building/BuildingDetails/Enclosure/Attics/Attic/AtticType/Attic/Conditioned) when we move to HPXML 3. We should include an extension to allow people to get that in v2.3. 

Let's do this as follows: To get a conditioned attic in HEScore, you set `Attic/AtticType` to "other" and then `Attic/extension/Conditioned` to "true". If it meets those two criteria, then translate as `cond_attic` in HEScore. If not that act as it normally does (i.e. "other" will cause the translation to fail.).

To Do:

- [ ] Update translation
- [ ] Write test of new capability
- [ ] Update docs to reflect change in behavior

Comments:

Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 0a4f5cf6..483e6512 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -11,7 +11,7 @@ jobs:
             pip install --user virtualenv
             python -m virtualenv env
             source env/bin/activate
-            pip install --progress-bar off .[dev]
+            pip install --progress-bar off .[test]
       - run:  &unittests
           name: Run Unittests
           command: |
diff --git a/docs/source/translation/zone_roof.rst b/docs/source/translation/zone_roof.rst
index b4f1e7df..4e1ed757 100644
--- a/docs/source/translation/zone_roof.rst
+++ b/docs/source/translation/zone_roof.rst
@@ -30,16 +30,19 @@ type according to the following mapping.
    unvented attic         vented_attic
    vented attic           vented_attic
    venting unknown attic  vented_attic
-   other                  *not translated*
+   other                  *see note below*
    =====================  ================
 
-.. warning:: 
+.. note::
    
-   There is no way to get a HEScore ``cond_attic``.
+   Currently, there's no existing HPXML element capturing a conditioned attic.
+   The only way to model a HEScore ``cond_attic`` is to specify HPXML Attic Type
+   to be ``other`` with an extra element ``Attic/extension/Conditioned`` to be
+   ``true``.
+
+   Otherwise, HPXML Attic Type ``other`` will not be translated and will
+   result in a translation error.
 
-.. note::   
-   
-   Items that are *not translated* will result in a translation error.
    
 HEScore can accept up to two attic/roof constructions. If there are more than
 two specified in HPXML, the properties of the ``Attic`` elements with
diff --git a/hescorehpxml/__init__.py b/hescorehpxml/__init__.py
index 056928d6..306fcba0 100644
--- a/hescorehpxml/__init__.py
+++ b/hescorehpxml/__init__.py
@@ -923,9 +923,13 @@ class HPXMLtoHEScoreTranslator(object):
             hpxml_attic_type = xpath(attic, 'h:AtticType/text()')
             atticd['rooftype'] = rooftypemap[hpxml_attic_type]
             if atticd['rooftype'] is None:
-                raise TranslationError(
-                    'Attic {}: Cannot translate HPXML AtticType {} to HEScore rooftype.'.format(atticid,
-                                                                                                hpxml_attic_type))
+                attc_is_cond = xpath(attic, 'h:extension/h:Conditioned/text()')
+                if attc_is_cond == 'true':
+                    atticd['rooftype'] = 'cond_attic'
+                else:
+                    raise TranslationError(
+                        'Attic {}: Cannot translate HPXML AtticType {} to HEScore rooftype.'.format(atticid,
+                                                                                                    hpxml_attic_type))
 
             # Roof color
             solar_absorptance = convert_to_type(float, xpath(roof, 'h:SolarAbsorptance/text()'))
diff --git a/setup.py b/setup.py
index f3143adf..5629d1aa 100644
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,7 @@ with open(path.join(here, 'README.md'), encoding='utf-8') as f:
 
 setup(
     name='hescore-hpxml',
-    version='5.0.0',
+    version='5.0.2',
     description='HPXML Translator for the HEScore API',
     long_description=long_description,
     long_description_content_type='text/markdown',
@@ -35,7 +35,10 @@ setup(
     ],
     keywords='home energy score hescore doe nrel',
     packages=['hescorehpxml'],
-    install_requires=['lxml'],
+    install_requires=[
+        'lxml',
+        'future',
+    ],
     extras_require={
         'dev': [
             'flake8',
@@ -43,7 +46,13 @@ setup(
             'sphinx',
             'sphinx_rtd_theme',
             'sphinx-autobuild',
-            'future'
+        ],
+        'test': [
+            'flake8',
+            'coverage',
+            'sphinx',
+            'sphinx_rtd_theme',
+            'sphinx-autobuild',
         ]
     },
     include_package_data=True,


Test patch:
diff --git a/tests/tests.py b/tests/tests.py
index 13fff2ac..ccc81832 100644
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -1970,6 +1970,33 @@ class TestHEScore2019Updates(unittest.TestCase, ComparatorBase):
         self.assertEqual(system['type'], 'tankless')
         self.assertAlmostEqual(system['energy_factor'], 0.7)
 
+    def test_conditioned_attic(self):
+        tr = self._load_xmlfile('house4')
+        attic = self.xpath('//h:Attic[h:SystemIdentifier/@id="attic1"]')
+        attic_type = self.xpath('//h:Attic[h:SystemIdentifier/@id="attic1"]/h:AtticType')
+        attic_type.text = 'other'
+        self.assertRaisesRegexp(
+            TranslationError,
+            r'Attic attic1: Cannot translate HPXML AtticType other to HEScore rooftype.',
+            tr.hpxml_to_hescore_dict
+        )
+        is_attic_cond = etree.SubElement(etree.SubElement(attic, tr.addns('h:extension')), tr.addns('h:Conditioned'))
+        is_attic_cond.text = 'true'
+        d = tr.hpxml_to_hescore_dict()
+        roof_type = d['building']['zone']['zone_roof'][0]['roof_type']
+        self.assertEqual(roof_type, 'cond_attic')
+        is_attic_cond.text = 'false'
+        self.assertRaisesRegexp(
+            TranslationError,
+            r'Attic \w+: Cannot translate HPXML AtticType other to HEScore rooftype.',
+            tr.hpxml_to_hescore_dict
+        )
+        attic_type.text = 'vented attic'
+        is_attic_cond.text = 'true'
+        d = tr.hpxml_to_hescore_dict()
+        roof_type = d['building']['zone']['zone_roof'][0]['roof_type']
+        self.assertEqual(roof_type, 'vented_attic')
+
 
 if __name__ == "__main__":
     unittest.main()