diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..e089571
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,63 @@
+# CanCurve development
+
+## Installation
+Typically, development should employ a virtual environment with pyqgis bindings.
+We usually develop to target the most recent QGIS LTR.
+Some additional packages for testing are specified in `./requirements.txt`
+
+### PYTHONPATH
+only the source directory should be included (`./CanCurve` not `./CanCurve/cancurve`)
+
+## Tests
+pytests are in `./tests`
+
+## Compiling
+the only compiling typically requied is when the qt `resources.qrc` file is changed. This needs to be ported to a python module (typically using `pyrcc5 -o resources.py resources.qrc` as in `./dev_tools/plug_compile.bat`)
+
+
+
+## Deploying Plugin
+
+### Active Development
+Typically a `dev` QGIS profile is maintained with a custom `QGIS_PLUGINPATH` pointed to the project source directory. This facilitates plugin updating with the `plugin reload` (ie no real deployment)
+
+### Pre-Release testing
+Pre-release testing (and full deployment) employ a zip of the plugin directory (see `./dev_tools/plug_zip.bat`):
+1) remove all `__pychace__`
+2) zip/archive the plugin directory
+
+This zip file can then be distributed using a git-hub release (upload the zip file to the github release... NOT the git repo tracking)
+
+
+### Full QGIS Repository release
+- [ ] create the plugin zip as above
+
+- [ ] in git-hub, create a new release tag (e.g., v1.2.0), summarize new features for developers. upload the zip file.
+
+- [ ] login to [plugins.qgis.org](https://plugins.qgis.org/accounts/login/?next=/plugins/my) using the CanFlood credentials (ask Nicky). Navigate to **Upload a plugin** and select the zip file.
+
+- [ ] In QGIS, refresh the repository and ensure that the new version is available (may take ~10mins for the version to be available). Upgrade and check that it works.
+
+- [ ] notify project team
+
+## Developing an Update
+
+the dev branch is where new features and fixes are collected and tested before release. The following should be executed on the dev branch in preparation for pushing to the main branch:
+
+- [ ] add/update documentation where applicable
+
+- [ ] backwards merge master into dev to capture any upstream changes (these should be minor and limited to documentation tweaks as all development is done on the dev branch)
+
+- [ ] ensure the version tag is updated on `.\cancurve\__init__.py`
+
+- [ ] update the README.md to summarize any new features for users
+
+- [ ] similarly update cancurve\metadata.txt
+
+- [ ] execute all tests. investigate warnings. fix errors.
+
+- [ ] perform a 'person test' by having a non-developer follow relevant tutorials. investigate warnings and fix errors.
+
+- [ ] Once these tests are complete **and passing**, a pull request should be completed and the dev branch merged into the main.
+
+- [ ] Follow the above **Deploying Plugin/Full QGIS Repository Release** on the main branch
\ No newline at end of file
diff --git a/LICENSE_TBD b/LICENSE_TBD
new file mode 100644
index 0000000..e69de29
diff --git a/README.md b/README.md
index bfa300c..5003fe1 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,11 @@ CanCurve is an open-source tool which can develop depth-damage (stage-damage) fu
+
+## Updates
+- 2024-05-09: small fixes and updates based on initial comments
+- 2024-05-01: initial working release (un tested) [v0.0.1](https://github.com/NRCan/CanCurve/releases/tag/v0.0.1)
+
## Installation
- Install [QGIS 3.34.5](https://download.qgis.org/downloads/) (with Qt 5.15.13)
@@ -12,6 +17,7 @@ CanCurve is an open-source tool which can develop depth-damage (stage-damage) fu
- in QGIS, `Manage and Install Plugins...` > `Install from ZIP` > select the downloaded file
- it is recommended to also install the **First Aid** plugin for more detailed error messages.
- it is recommended to set up the QGIS Debug Log file as shown [here](https://stackoverflow.com/a/61669864/9871683)
+- CanCurve backend and project data is implemented in SQLite relational databases. For enhanced customization and debugging, it is recommended to install a SQLite viewer + editor like [DB Browser for SQLite](https://sqlitebrowser.org/) for working with these files.
diff --git a/cancurve/__init__.py b/cancurve/__init__.py
index d1873a4..d7ce530 100644
--- a/cancurve/__init__.py
+++ b/cancurve/__init__.py
@@ -1,6 +1,11 @@
+#===============================================================================
+# plugin metadata
+#===============================================================================
+__version__='0.0.2'
-__version__='0.0'
-
+#===============================================================================
+# plugin entry point
+#===============================================================================
# noinspection PyPep8Naming
def classFactory(iface): # pylint: disable=invalid-name
"""Load CanCurve class from file CanCurve.
@@ -10,4 +15,23 @@ def classFactory(iface): # pylint: disable=invalid-name
"""
#
from .plugin import CanCurve
- return CanCurve(iface)
\ No newline at end of file
+ return CanCurve(iface)
+
+#===============================================================================
+# dependency check
+#===============================================================================
+
+
+
+import importlib, warnings
+
+def check_package(package_name):
+ spec = importlib.util.find_spec(package_name)
+ if spec is not None:
+ print(f'module {package_name} is installed')
+ else:
+ warnings.warn(f'module \'{package_name}\' not installed')
+
+
+check_package('openpyxl')
+
diff --git a/cancurve/bldgs/assertions.py b/cancurve/bldgs/assertions.py
index 599ab0e..70d6224 100644
--- a/cancurve/bldgs/assertions.py
+++ b/cancurve/bldgs/assertions.py
@@ -9,6 +9,7 @@
import pandas as pd
from .parameters import colns_index, colns_dtypes, bldg_meta_rqmt_df
+from ..hp.basic import view_web_df as view
expected_tables_base = ['project_meta','project_settings','c00_bldg_meta', 'c00_cost_items','c00_drf']
@@ -39,9 +40,10 @@ def assert_ci_df(df):
# Check data types
for coln, dstr in df.dtypes.items():
if coln not in colns_dtypes:
- raise IOError(f'Unrecognized column name in estimate data: \'{coln}\'')
- if dstr != colns_dtypes[coln]: # More specific check
- raise AssertionError(f"Incorrect data type for column '{coln}'. Expected: {colns_dtypes[coln]}, Found: {dstr}")
+ print(f'Unrecognized column name in estimate data: \'{coln}\'')
+ else:
+ if dstr != colns_dtypes[coln]: # More specific check
+ raise AssertionError(f"Incorrect data type for column '{coln}'. Expected: {colns_dtypes[coln]}, Found: {dstr}")
#===============================================================================
@@ -118,6 +120,8 @@ def assert_drf_df(df):
Raises:
TypeError: If the input is not a DataFrame or columns have non-float types.
KeyError: If the DataFrame's index names are incorrect.
+
+ view(df)
"""
# Check if it's a DataFrame
@@ -128,14 +132,15 @@ def assert_drf_df(df):
if not set(df.index.names).difference(['cat', 'sel', 'bldg_layout']) == set():
raise KeyError("Incorrect index names in DataFrame")
- # Check data types
+ # Check the columns all conform to float depths
if not 'float' in df.columns.dtype.name:
- raise TypeError('bad type on columns')
+ raise TypeError(f'DRF column headers expected as dtype float. instead got \'{df.columns.dtype.name}\'')
# Check data types (more accurate)
- for col in df.columns:
- if df[col].dtype != 'float64': # Assuming you want specifically float64
- raise TypeError(f"Column '{col}' is not a float type")
+
+ for i, col in enumerate(df.columns):
+ if not 'float' in df[col].dtype.name: # Assuming you want specifically float64
+ raise TypeError(f'DRF column \'{col}\' ({i}) expected as dtype float. instead got \'{df[col].dtype}\'')
def assert_bldg_meta_d(bldg_meta):
"""check the bldg_meta_d meets expectations"""
diff --git a/cancurve/bldgs/bldg_meta_rqmts.csv b/cancurve/bldgs/bldg_meta_rqmts.csv
index d5488f2..46583e5 100644
--- a/cancurve/bldgs/bldg_meta_rqmts.csv
+++ b/cancurve/bldgs/bldg_meta_rqmts.csv
@@ -1,39 +1,38 @@
-varName_core,varName_ui,varName_canflood,type,required_core,required_canflood,default_canflood,widgetName,case1,case2
-,,tag,str,FALSE,TRUE,?,,test_case1,test_case2
-,,location,str,FALSE,FALSE,?,,,
-,,date,str,FALSE,FALSE,?,,,
-,,source,str,FALSE,FALSE,?,,,
-,currency,impact_units,str,FALSE,TRUE,$CAD,currency_ComboBox,,
-,costBasis,impact_var,str,FALSE,FALSE,damage,costBasis_ComboBox,,
-,,exposure_units,str,FALSE,FALSE,m,comboBox_tab3dataInput_expoUnits,,
-,,exposure_var,str,FALSE,FALSE,flood depth above floor,,,
-,,scale_var,str,FALSE,FALSE,building footprint,,,
-bldg_layout,buildingLayout,,str,TRUE,FALSE,,buildingLayout_ComboBox,default,default
-,basementHeight,,float,FALSE,FALSE,,basementHeight_DoubleSpinBox,1.8,2.1
-,basementHeightUnits,,str,FALSE,FALSE,,basementHeightUnits_ComboBox,m,m
-basement_height_m,,,float,TRUE,FALSE,,,1.8,2.1
-,sizeOrAreaValue,,float,FALSE,FALSE,,sizeOrAreaValue_DoubleSpinBox,232.1,232.2
-,sizeOrAreaUnits,scale_units,str,FALSE,FALSE,m2,sizeOrAreaUnits_ComboBox,m2,m2
-scale_value_m2,,,float,TRUE,FALSE,,,232.1,232.2
-,,exposure,str,FALSE,TRUE,impact,,,
-,occupancyClassification,,str,FALSE,FALSE,,occupancyClassification_ComboBox,,
-,subClassification,,str,FALSE,FALSE,,subClassification_ComboBox,,
-,storeys,,str,FALSE,FALSE,,storeys_ComboBox,,
-,heatingType,,str,FALSE,FALSE,,heatingType_ComboBox,,
-,coolingType,,str,FALSE,FALSE,,coolingType_ComboBox,,
-,garageType,,str,FALSE,FALSE,,garageType_ComboBox,,
-,garageSize,,str,FALSE,FALSE,,garageSize_ComboBox,,
-,buildingLayout,,str,FALSE,FALSE,,buildingLayout_ComboBox,,
-,foundationType,,str,FALSE,FALSE,,foundationType_ComboBox,,
-,qualityOfBuildingMaterials,,str,FALSE,FALSE,,qualityOfBuildingMaterials_ComboBox,,
-,provinceTerritory,,str,FALSE,FALSE,,provinceTerritory_ComboBox,,
-,taxesIncluded,,str,FALSE,FALSE,,taxesIncluded_ComboBox,,
-,priceListSource,,str,,,,,,
-,country,,str,,,,,,
-,BathroomCount,,int,,,,,,
-,pricingDate,,str,,,,,,
-,BedroomCount,,int,,,,,,
-,basementFinish,,str,,,,,,
-,createdBy,,str,,,,,,
-,locationCityTownRegion,,str,,,,,,
-,yearOfBuildingConstruction,,int,,,,,,
+varName_core,varName_ui,varName_canflood,type,required_core,required_canflood,default_canflood,widgetName,case1,case2,case3
+,,tag,str,FALSE,TRUE,?,,test_case1,test_case2,heather_0509
+,,location,str,FALSE,FALSE,?,,,,
+,,date,str,FALSE,FALSE,?,,,,
+,,source,str,FALSE,FALSE,?,,,,
+,currency,impact_units,str,FALSE,TRUE,$CAD,currency_ComboBox,,,
+,costBasis,impact_var,str,FALSE,FALSE,damage,costBasis_ComboBox,,,Depreciated Costs
+,,exposure_units,str,FALSE,FALSE,m,comboBox_tab3dataInput_expoUnits,,,
+,,exposure_var,str,FALSE,FALSE,flood depth above floor,,,,
+,,scale_var,str,FALSE,FALSE,building footprint,,,,
+bldg_layout,buildingLayout,,str,TRUE,FALSE,,buildingLayout_ComboBox,default,default,default
+,basementHeight,,float,FALSE,FALSE,,basementHeight_DoubleSpinBox,1.8,2.1,0
+,basementHeightUnits,,str,FALSE,FALSE,,basementHeightUnits_ComboBox,m,m,m
+basement_height_m,,,float,TRUE,FALSE,,,1.8,2.1,0
+,sizeOrAreaValue,,float,FALSE,FALSE,,sizeOrAreaValue_DoubleSpinBox,232.1,232.2,344
+,sizeOrAreaUnits,scale_units,str,FALSE,FALSE,m2,sizeOrAreaUnits_ComboBox,m2,m2,m2
+scale_value_m2,,,float,TRUE,FALSE,,,232.1,232.2,344
+,,exposure,str,FALSE,TRUE,impact,,,,
+,occupancyClassification,,str,FALSE,FALSE,,occupancyClassification_ComboBox,,,Commercial
+,subClassification,,str,FALSE,FALSE,,subClassification_ComboBox,,,Construction
+,storeys,,str,FALSE,FALSE,,storeys_ComboBox,,,1
+,BedroomCount,,int,FALSE,FALSE,,BedroomCount_QSpinBox,,,0
+,BathroomCount,,float,FALSE,FALSE,,BathroomCount_DoubleSpinBox,,,1
+,heatingType,,str,FALSE,FALSE,,heatingType_ComboBox,,,Forced Air - Gas
+,coolingType,,str,FALSE,FALSE,,coolingType_ComboBox,,,Central air
+,garageType,,str,FALSE,FALSE,,garageType_ComboBox,,,None
+,garageSize,,str,FALSE,FALSE,,garageSize_ComboBox,,,
+,foundationType,,str,FALSE,FALSE,,foundationType_ComboBox,,,other
+,qualityOfBuildingMaterials,,str,FALSE,FALSE,,qualityOfBuildingMaterials_ComboBox,,,Average
+,taxesIncluded,,str,FALSE,FALSE,,taxesIncluded_ComboBox,,,
+,priceListSource,,str,FALSE,FALSE,,priceListSource_LineEdit,,,exactimate
+,country,,str,FALSE,FALSE,Canada,country_ComboBox,,,Canada
+,provinceTerritory,,str,FALSE,FALSE,,provinceTerritory_ComboBox,,,ON
+,pricingDate,,str,FALSE,FALSE,,,,,
+,basementFinish,,float,FALSE,FALSE,,basementFinish_DoubleSpinBox,,,100
+,createdBy,,str,FALSE,FALSE,,createdBy_LineEdit,,,hmcgrath
+,locationCityTownRegion,,str,FALSE,FALSE,,,,,
+,yearOfBuildingConstruction,,int,FALSE,FALSE,,yearOfBuildingConstruction_SpinBox,,,2500
diff --git a/cancurve/bldgs/cc_bldgs_dialog.ui b/cancurve/bldgs/cc_bldgs_dialog.ui
index 0104943..b39ccd5 100644
--- a/cancurve/bldgs/cc_bldgs_dialog.ui
+++ b/cancurve/bldgs/cc_bldgs_dialog.ui
@@ -6,7 +6,7 @@
0
0
- 784
+ 800
800
@@ -140,21 +140,22 @@ QTabBar::tab:selected { /* Style for selected tabs */
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:14px; font-weight:400; font-style:normal;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">Welcome to CanCurve! This tool is designed to create depth (stage) damage functions from user input.</span></p>
-<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/plugins/cancurve/img/icon.png" /></p>
-<p align="center" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;"><br /></p>
-<p align="center" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;"><br /></p>
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">In order for this tool to successfully run, the inputs you will need:</span></p>
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">- </span><span style=" font-size:12pt; font-weight:600;">General Data</span><span style=" font-size:12pt;">: On the building details tab, there are a series of dropdown menus to fill in, which will populate metadata fields about the depth damage curve. Some of the fields may be used in the calculation process.</span></p>
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">- </span><span style=" font-size:12pt; font-weight:600;">Cost Information: </span><span style=" font-size:12pt;">CSV file of Items & Costs.Click HERE for an example file</span></p>
-<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;"><br /></p>
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">CanCurve accepts the following additional files for more customization:</span></p>
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt; font-weight:600;">- Depth-Replacement-Factor dataset</span><span style=" font-size:12pt;">: By default, the CanCurve DRF dataset is used .</span></p>
-<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;"><br /></p>
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">Click the tabs at the top of the screen to navigate through the screens and create your CanCurve </span></p>
-<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;"> </span></p>
-<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;"> </span></p>
-<p align="center" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;"><br /></p></body></html>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">Welcome to CanCurve's </span><span style=" font-size:12pt; font-style:italic;">Buildings</span><span style=" font-size:12pt;"> tool! This tool is designed to create </span><span style=" font-size:12pt; font-style:italic;">Depth Damage Functions</span><span style=" font-size:12pt;"> (DDF) for Canadian buildings.</span></p>
+<p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/plugins/cancurve/img/icon.png" /></p>
+<p align="center" style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">The following inputs are required:</span></p>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:16px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt; font-weight:600;">Building Metadata</span><span style=" font-size:12pt;">: On the </span><span style=" font-size:12pt; font-style:italic;">Building Metadata</span><span style=" font-size:12pt;"> tab, standard flood-related building properties can be specified. This information will be used to populate metadata on the resulting DDF. Some of the fields may be used in the calculation process as well.</span></p>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:16px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt; font-weight:600;">Cost Information: </span><span style=" font-size:12pt;">CSV file of restoration items and their costs. Click </span><a href="https://github.com/NRCan/CanCurve/blob/main/tests/data/bldgs/case1/R_1-L-BD-CU_ABCA.csv"><span style=" font-size:12pt; text-decoration: underline; color:#0000ff;">here</span></a><span style=" font-size:12pt;"> for an example.</span><span style=" font-size:14px;"><br /></span></p>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">The following inputs are optional:</span></p>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:16px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt; font-weight:600;">Depth-Replacement-Factor (DRF) dataset</span><span style=" font-size:12pt;">: By default, the DRF dataset instaled with CanCurve is used.</span></p>
+<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:16px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;"><br /></p>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">Navigate through the tabs at the top of the window from left-to-right to create your own DDF.</span></p></body></html>
+
+
+ true
+
+
+ true
@@ -200,7 +201,7 @@ p, li { white-space: pre-wrap; }
- Building Details
+ Building Metadata
-
@@ -219,8 +220,8 @@ p, li { white-space: pre-wrap; }
0
- -118
- 729
+ 0
+ 745
949
@@ -311,7 +312,11 @@ QGroupBox {
-
-
+
+
+ 1
+
+
-
@@ -410,7 +415,11 @@ QGroupBox {
-
-
+
+
+ 1
+
+
-
@@ -431,6 +440,9 @@ QGroupBox {
-
+
+ 0
+
100.000000000000000
@@ -611,7 +623,11 @@ QGroupBox {
-
-
+
+
+ yyyy-MM-dd
+
+
-
@@ -775,7 +791,7 @@ QGroupBox {
-
-
+
Browse
@@ -820,7 +836,7 @@ QGroupBox {
- RCV
+ Cost
@@ -893,7 +909,7 @@ QGroupBox {
-
-
+
Browse
@@ -1592,7 +1608,7 @@ QLabel {
-
-
+
0
0
@@ -1617,12 +1633,12 @@ QLabel {
0
0
- 578
- 55
+ 575
+ 68
-
+
0
0
@@ -1636,21 +1652,21 @@ QLabel {
16777215
- 55
+ 100
-
-
+
0
0
- 0
+ 500
50
@@ -1684,41 +1700,67 @@ QLabel {
-
-
-
-
-
-
-
- 0
- 0
-
-
-
- Cancel
-
-
- false
-
-
-
+
-
-
-
-
- 0
- 0
-
-
-
- Close
-
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Cancel
+
+
+ false
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Close
+
+
+
+
+ -
+
+
-
+
+
+ QWidget { /* Targets most widgets */
+ font-size: 8px;
+}
+
+/* For more specific elements */
+QLabel {
+ font-size: 8px;
+}
+
+
+
+ v?
+
+
+
+
+
diff --git a/cancurve/bldgs/core.py b/cancurve/bldgs/core.py
index 7fbed26..3bf2149 100644
--- a/cancurve/bldgs/core.py
+++ b/cancurve/bldgs/core.py
@@ -832,8 +832,10 @@ def c00_setup_project(
log.debug(f'loaded {len(fc_ser)} fixed costs')
#check intersect
- if not set(fc_ser.index).difference(ci_df['story'].unique())==set():
- raise KeyError(f'fixed cost stories do not match CostItems')
+ miss = set(fc_ser.index.values).difference(ci_df['story'].unique())
+ if not miss==set():
+ raise KeyError(f'\'Storey\' values specified in the Fixed Costs table do not match those in the Cost Item Dataset'+\
+ '\n ensure fixed costs are provided for each storey')
else:
fc_ser=None
diff --git a/cancurve/bldgs/dialog.py b/cancurve/bldgs/dialog.py
index 36aed77..361c06e 100644
--- a/cancurve/bldgs/dialog.py
+++ b/cancurve/bldgs/dialog.py
@@ -44,7 +44,7 @@
from ..config import dev_mode
-from ..hp.basic import convert_to_number
+from ..hp.basic import convert_to_number, force_open_dir
from ..hp.plug import plugLogger
from ..hp.qt import (
DialogQtBasic, get_formLayout_data, get_gridLayout_data, get_tabelWidget_data,
@@ -56,19 +56,13 @@
)
from .parameters_ui import building_details_options_d
+
#===============================================================================
# helpers-------
#===============================================================================
-
-
-
-
-
-
-
-
+
#===============================================================================
# Main Dialog----------
@@ -161,6 +155,9 @@ def close_dialog():
self.close_pushButton.clicked.connect(close_dialog)
self.cancel_pushButton.clicked.connect(self.action_cancel_process)
+ from cancurve import __version__
+ self.label_version.setText(f'v{__version__}')
+
#=======================================================================
# development-----
#=======================================================================
@@ -242,6 +239,62 @@ def populate_ui():
self.lineEdit_tab3dataInput_drfFp.setText(drf_db_default_fp)
self.lineEdit_wdir.setText(home_dir)
+ self.pushButton_wd_open.clicked.connect(
+ lambda: force_open_dir(self.lineEdit_wdir.text())
+ )
+
+ #working directory browse
+ def pushButton_wd_QFileDialog():
+ filename = QFileDialog.getExistingDirectory(
+ self, # Parent widget (your dialog)
+ "Select working directory", # Dialog title
+ home_dir, # Initial directory (optional, use current working dir by default)
+ #"tabular data files;;Comma Separated Values (*.csv)" # Example file filters
+ )
+
+ if filename =='':
+ self.logger.debug('user cancelled QFileDialog')
+ return
+
+ #needed so we can have a single test for both types of file dialogs
+ if isinstance(filename, tuple):
+ filename=filename[0]
+
+ if filename:
+ self.lineEdit_wdir.setText(filename)
+
+ self.pushButton_wd.clicked.connect(pushButton_wd_QFileDialog)
+
+ #cost item browse
+ def pushButton_tab3dataInput_cifp_QFileDialog():
+ filename, _ = QFileDialog.getOpenFileName(
+ self, # Parent widget (your dialog)
+ "Open Cost-Item file", # Dialog title
+ home_dir, # Initial directory (optional, use current working dir by default)
+ "comma separated values (*.csv)" # Example file filters
+ )
+ if filename:
+ self.lineEdit_tab3dataInput_cifp.setText(filename)
+
+
+ self.pushButton_tab3dataInput_cifp.clicked.connect(pushButton_tab3dataInput_cifp_QFileDialog)
+
+
+ #DRF browse
+ def pushButton_tab3dataInput_drfFp_QFileDialog():
+ filename, _ = QFileDialog.getOpenFileName(
+ self, # Parent widget (your dialog)
+ "Open DRF file", # Dialog title
+ home_dir, # Initial directory (optional, use current working dir by default)
+ "sqlite database files (*.db)" # Example file filters
+ )
+ if filename:
+ self.lineEdit_tab3dataInput_drfFp.setText(filename)
+
+
+ self.pushButton_tab3dataInput_drfFp.clicked.connect(pushButton_tab3dataInput_drfFp_QFileDialog)
+
+
#=======================================================================
# Tab: 04 Create Curve---------
@@ -293,8 +346,7 @@ def proj_db_browse_QFileDialog():
)
if filename:
self.lineEdit_tab4actions_projdb.setText(filename)
-
- print('\'proj_db_browse\' finished')
+
self.pushButton_tab4actions_browse.clicked.connect(proj_db_browse_QFileDialog)
@@ -409,9 +461,19 @@ def action_tab4actions_run(self):
# run actions-------
#=======================================================================
+ #=======================================================================
+ # step 1
+ #=======================================================================`
step_log(1)
- self._run_c00_setup_project(logger=log, out_dir=out_dir)
+ _, _, _, err_msg = self._run_c00_setup_project(logger=log, out_dir=out_dir)
+
+ if not err_msg is None:
+ log.warning(err_msg)
+ return
+ #=======================================================================
+ # step 2
+ #=======================================================================
step_log(2)
self._run_c01_join_drf(logger=log)
@@ -423,6 +485,8 @@ def action_tab4actions_run(self):
log.push(f'workflow complete and DDF output to\n {ofp}')
+ return
+
def action_tab4actions_step1(self):
@@ -561,7 +625,7 @@ def _run_c00_setup_project(self,
log.info(f'Step 1 complete w/ {ci_df.shape}')
- return ci_df, drf_df, ofp
+ return ci_df, drf_df, ofp, err_msg
diff --git a/cancurve/bldgs/dialog_test_scripts.py b/cancurve/bldgs/dialog_test_scripts.py
index ec60f66..48c067a 100644
--- a/cancurve/bldgs/dialog_test_scripts.py
+++ b/cancurve/bldgs/dialog_test_scripts.py
@@ -32,14 +32,15 @@
#test_data_dir_master = os.path.join(parent_tdata_dir, 'bldgs')
-test_cases_l = ['case1', 'case2']
+
fixed_costs_master_d = {
'case1':{0:10000, -1:8000},
- 'case2':None,
+ 'case2':None,
+ 'case3':{-1:0, 0:25000}
}
-
+test_cases_l = list(fixed_costs_master_d.keys())
#===============================================================================
# helpers------
#===============================================================================
@@ -58,17 +59,31 @@ def set_tab2bldgDetils(dialog, testCase):
df = bldg_meta_rqmt_df.loc[:, ['varName_ui', 'widgetName', 'type'] + test_cases_l].dropna(subset='varName_ui').set_index('varName_ui')
#loop through and change the combobox to match whats in the dictionary
for k, row in df.iterrows():
- if not pd.isnull(row[testCase]):
- v = row[testCase]
- elif k in building_details_options_d:
- v = building_details_options_d[k][0] #just take first
- else:
- #print('continue')
- continue #skip this one
- #v = str(v_raw)
- #comboBox = dialog._get_child(f'{k}_ComboBox', childType=QtWidgets.QComboBox)
- widget = getattr(dialog, row['widgetName'])
- #check if the requested value is one of the comboBox's options
- if isinstance(widget, QComboBox):
- assert_string_in_combobox(widget, v)
- set_widget_value(widget, v)
\ No newline at end of file
+
+ try:
+ if not pd.isnull(row[testCase]):
+ v = row[testCase]
+ elif k in building_details_options_d:
+ v = building_details_options_d[k][0] #just take first
+ else:
+ #print('continue')
+ continue #skip this one
+ #v = str(v_raw)
+ assert hasattr(dialog, row['widgetName']), f'bad widgetname: %s'%row['widgetName']
+ widget = getattr(dialog, row['widgetName'])
+ #check if the requested value is one of the comboBox's options
+ if isinstance(widget, QComboBox):
+ assert_string_in_combobox(widget, v)
+ set_widget_value(widget, v)
+ except Exception as e:
+ raise IOError(f'failed to set element {k} on the building details tab w/\n {e}')
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cancurve/bldgs/parameters_ui.py b/cancurve/bldgs/parameters_ui.py
index a0a3baa..2dcaf65 100644
--- a/cancurve/bldgs/parameters_ui.py
+++ b/cancurve/bldgs/parameters_ui.py
@@ -12,10 +12,10 @@
building_details_options_d = {
#general
'occupancyClassification': ['Residential', 'Commercial', 'Industrial', 'Other'],
- 'subClassification': ['Single Family', 'Construction', 'Duplex', 'entertainment and recreation'],
- 'storeys': ['1', '2', '3', 'Split'],
+ 'subClassification': ['Single Family', 'Construction', 'Duplex', 'Entertainment and Recreation'],
+ 'storeys': ['1', '2', 'Split', 'Multi-story'],
- 'heatingType': ['baseboard-electric', 'baseboard-hot water', 'Forced air - electric', 'forced air-gas'],
+ 'heatingType': ['Baseboard-electric', 'Baseboard-hot Water', 'Forced Air - Electric', 'Forced Air - Gas', 'Other'],
'coolingType': ['Central air', 'Heat pump'],
'garageType': ['Yes, Attached', 'Yes, Detached', 'None'],
'garageSize': ['Single', 'Single Plus', 'Double', 'Triple'],
@@ -50,8 +50,8 @@
"YT" # Yukon
],
'taxesIncluded': ['PST', 'PST & GST', 'GST', 'None'],
- 'currency':['$CAD', '$USD'],
- 'costBasis':['replacement cost', 'depreciated costs']
+ 'currency':['$CAD', '$USD', 'Other'],
+ 'costBasis':['Replacement Cost', 'Depreciated Costs']
}
diff --git a/cancurve/dev_test_data/case1/c00/c00_setup_project_result.pkl b/cancurve/dev_test_data/case1/c00/c00_setup_project_result.pkl
deleted file mode 100644
index 0a4a245..0000000
--- a/cancurve/dev_test_data/case1/c00/c00_setup_project_result.pkl
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6c7f17079df1f397e7643359bd300bb9f8ec4d4b63e968db2fd453eb4247f04e
-size 67982
diff --git a/cancurve/dev_test_data/case1/c01/c01_join_drf.pkl b/cancurve/dev_test_data/case1/c01/c01_join_drf.pkl
deleted file mode 100644
index 80f74f2..0000000
--- a/cancurve/dev_test_data/case1/c01/c01_join_drf.pkl
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ad97bcfcfc0d1e93b262563718d9c2b7a9001e43496e4a546e79ebcfe2d204e4
-size 158710
diff --git a/cancurve/dev_test_data/case1/c02/c02_group_story.pkl b/cancurve/dev_test_data/case1/c02/c02_group_story.pkl
deleted file mode 100644
index 7170d2f..0000000
--- a/cancurve/dev_test_data/case1/c02/c02_group_story.pkl
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c4eb53b30b017b27892492db7bc1b91c4fbdb2eeb8c70cc99e356e13e2e5fb82
-size 1733
diff --git a/cancurve/dev_test_data/case1/c03/c03_export.pkl b/cancurve/dev_test_data/case1/c03/c03_export.pkl
deleted file mode 100644
index 3a10ea9..0000000
--- a/cancurve/dev_test_data/case1/c03/c03_export.pkl
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:29ff4c9150430b7d66f5e8bf9ecbccc975807af204f4ac05d0ee520999229cf0
-size 1646
diff --git a/cancurve/dev_test_data/case2/c00/c00_setup_project_result.pkl b/cancurve/dev_test_data/case2/c00/c00_setup_project_result.pkl
deleted file mode 100644
index 92ae8bf..0000000
--- a/cancurve/dev_test_data/case2/c00/c00_setup_project_result.pkl
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7f43414fcb62b1c4ef6404c190c621048a682d4db2bfb80a8cf7eca62b7f0e8d
-size 37019
diff --git a/cancurve/dev_test_data/case3/C_1-L-NoB-ST_ABCA.csv b/cancurve/dev_test_data/case3/C_1-L-NoB-ST_ABCA.csv
new file mode 100644
index 0000000..8447d4c
--- /dev/null
+++ b/cancurve/dev_test_data/case3/C_1-L-NoB-ST_ABCA.csv
@@ -0,0 +1,473 @@
+cat,sel,group_code,rcv,story,desc
+DOR,AV,Bath,493.59,0,Interior door unit
+DOR,AV,Bath3,493.59,0,Interior door unit
+DOR,AV,Bath2,493.59,0,Interior door unit
+DOR,AV,LaundryRoom,493.59,0,Interior door unit
+DOR,AV,WalkInCloset,493.59,0,Interior door unit
+DOR,AV,Bedroom,493.59,0,Interior door unit
+DRY,1/2+,Bath,178.52,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bath3,1348.04,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bath3,353.9,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bath2,753.32,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bath2,111.81,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,LaundryRoom,1348.04,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,LaundryRoom,353.9,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,UtilityRoom,1645.4,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,UtilityRoom,528.91,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,WalkInCloset,1645.4,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,WalkInCloset,528.91,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,FamilyRoom,2246.71,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,FamilyRoom,1350.16,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom2,1949.35,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom2,744.24,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Kitchen,1949.35,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Kitchen,731.36,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom3,1949.35,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom3,744.24,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom,1493.39,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom,439.45,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Nook,1493.39,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Nook,439.45,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Hallway2,1572.69,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Hallway2,397.88,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom4,2246.71,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom4,990.46,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,LivingRoom,2550.71,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,LivingRoom,1756.45,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Hallway,1420.75,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Hallway,263.31,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,DiningRoom,2246.71,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,DiningRoom,954.87,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+ELE,110,Kitchen,105.88,0,"110 volt copper wiring run, box and outlet"
+ELE,BPNL200,mech_single,2026.63,0,Breaker panel - 200 amp
+ELE,GFI,general_main,44.8,0,Ground fault interrupter (GFI) outlet
+ELE,GFI,Bath,44.8,0,Ground fault interrupter (GFI) outlet
+ELE,GFI,Bath3,89.62,0,Ground fault interrupter (GFI) outlet
+ELE,GFI,Bath2,44.8,0,Ground fault interrupter (GFI) outlet
+ELE,GFI,LaundryRoom,44.8,0,Ground fault interrupter (GFI) outlet
+ELE,GFI,Kitchen,89.62,0,Ground fault interrupter (GFI) outlet
+ELE,GFI,general_main,44.8,0,Ground fault interrupter (GFI) outlet
+ELE,LV,general,58.08,0,Phone / low voltage copper wiring
+ELE,LV,general,58.08,0,Phone / low voltage copper wiring
+ELE,OL,general,49.54,0,"Phone, TV, or speaker outlet"
+ELE,OL,general,99.08,0,"Phone, TV, or speaker outlet"
+ELE,OL220,LaundryRoom,45.46,0,220 volt outlet
+ELE,OL220,Kitchen,45.46,0,220 volt outlet
+ELE,OS,EntryFoyer,38.74,0,Outlet
+ELE,OS,general_main,19.36,0,Outlet
+ELE,OS,Bath,38.74,0,Outlet
+ELE,OS,Bath3,38.74,0,Outlet
+ELE,OS,Bath2,38.74,0,Outlet
+ELE,OS,LaundryRoom,19.36,0,Outlet
+ELE,OS,UtilityRoom,116.2,0,Outlet
+ELE,OS,WalkInCloset,38.74,0,Outlet
+ELE,OS,FamilyRoom,154.94,0,Outlet
+ELE,OS,Bedroom2,116.2,0,Outlet
+ELE,OS,Kitchen,77.48,0,Outlet
+ELE,OS,Kitchen,38.74,0,Outlet
+ELE,OS,Bedroom3,116.2,0,Outlet
+ELE,OS,Bedroom,116.2,0,Outlet
+ELE,OS,Nook,77.48,0,Outlet
+ELE,OS,Hallway2,38.74,0,Outlet
+ELE,OS,Bedroom4,116.2,0,Outlet
+ELE,OS,LivingRoom,154.94,0,Outlet
+ELE,OS,general_main,19.36,0,Outlet
+ELE,OS,Hallway,38.74,0,Outlet
+ELE,OS,DiningRoom,96.84,0,Outlet
+ELE,REWIRE,general,12720,0,Rewire\wire - avg. residence - boxes & wiring
+ELE,SMOKE,general,80.96,0,Smoke detector
+ELE,SMOKE,Bedroom2,80.96,0,Smoke detector
+ELE,SMOKE,Bedroom3,80.96,0,Smoke detector
+ELE,SMOKE,Bedroom,80.96,0,Smoke detector
+ELE,SMOKE,Hallway2,80.96,0,Smoke detector
+ELE,SMOKE,Bedroom4,80.96,0,Smoke detector
+ELE,SMOKE,Hallway,80.96,0,Smoke detector
+ELE,XOS,general_main,56.68,0,Exterior outlet or switch
+FCC,AV,EntryFoyer,115.99,0,Carpet
+FCC,AV,Bath,47.11,0,Carpet
+FCC,AV,Bath3,93.39,0,Carpet
+FCC,AV,Bath2,29.52,0,Carpet
+FCC,AV,LaundryRoom,93.39,0,Carpet
+FCC,AV,UtilityRoom,139.65,0,Carpet
+FCC,AV,WalkInCloset,139.65,0,Carpet
+FCC,AV,FamilyRoom,252.03,0,Carpet
+FCC,AV,Bedroom2,196.46,0,Carpet
+FCC,AV,Kitchen,193.06,0,Carpet
+FCC,AV,Bedroom3,196.46,0,Carpet
+FCC,AV,Bedroom,115.99,0,Carpet
+FCC,AV,Nook,115.99,0,Carpet
+FCC,AV,Hallway2,105,0,Carpet
+FCC,AV,Bedroom4,261.48,0,Carpet
+FCC,AV,LivingRoom,327.92,0,Carpet
+FCC,AV,Hallway,69.5,0,Carpet
+FCC,AV,DiningRoom,252.03,0,Carpet
+FCC,PAD,EntryFoyer,16.89,0,Carpet pad
+FCC,PAD,Bath,6.85,0,Carpet pad
+FCC,PAD,Bath3,13.59,0,Carpet pad
+FCC,PAD,Bath2,4.3,0,Carpet pad
+FCC,PAD,LaundryRoom,13.59,0,Carpet pad
+FCC,PAD,UtilityRoom,20.32,0,Carpet pad
+FCC,PAD,WalkInCloset,20.32,0,Carpet pad
+FCC,PAD,FamilyRoom,36.69,0,Carpet pad
+FCC,PAD,Bedroom2,28.59,0,Carpet pad
+FCC,PAD,Kitchen,28.1,0,Carpet pad
+FCC,PAD,Bedroom3,28.59,0,Carpet pad
+FCC,PAD,Bedroom,16.89,0,Carpet pad
+FCC,PAD,Nook,16.89,0,Carpet pad
+FCC,PAD,Hallway2,15.28,0,Carpet pad
+FCC,PAD,Bedroom4,38.06,0,Carpet pad
+FCC,PAD,LivingRoom,47.74,0,Carpet pad
+FCC,PAD,Hallway,10.11,0,Carpet pad
+FCC,PAD,DiningRoom,36.69,0,Carpet pad
+FCV,AV,EntryFoyer,119.16,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bath,48.38,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bath3,95.97,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bath2,30.34,0,Vinyl floor covering (sheet goods)
+FCV,AV,LaundryRoom,95.97,0,Vinyl floor covering (sheet goods)
+FCV,AV,UtilityRoom,143.48,0,Vinyl floor covering (sheet goods)
+FCV,AV,WalkInCloset,143.48,0,Vinyl floor covering (sheet goods)
+FCV,AV,FamilyRoom,258.95,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bedroom2,201.85,0,Vinyl floor covering (sheet goods)
+FCV,AV,Kitchen,198.35,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bedroom3,201.85,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bedroom,119.16,0,Vinyl floor covering (sheet goods)
+FCV,AV,Nook,119.16,0,Vinyl floor covering (sheet goods)
+FCV,AV,Hallway2,107.88,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bedroom4,268.65,0,Vinyl floor covering (sheet goods)
+FCV,AV,LivingRoom,336.92,0,Vinyl floor covering (sheet goods)
+FCV,AV,Hallway,71.41,0,Vinyl floor covering (sheet goods)
+FCV,AV,DiningRoom,258.95,0,Vinyl floor covering (sheet goods)
+FCV,PLK,EntryFoyer,144.57,0,Vinyl plank flooring
+FCV,PLK,Bath,58.69,0,Vinyl plank flooring
+FCV,PLK,Bath3,116.47,0,Vinyl plank flooring
+FCV,PLK,Bath2,36.81,0,Vinyl plank flooring
+FCV,PLK,LaundryRoom,116.47,0,Vinyl plank flooring
+FCV,PLK,UtilityRoom,173.99,0,Vinyl plank flooring
+FCV,PLK,WalkInCloset,173.99,0,Vinyl plank flooring
+FCV,PLK,FamilyRoom,314.14,0,Vinyl plank flooring
+FCV,PLK,Bedroom2,244.88,0,Vinyl plank flooring
+FCV,PLK,Kitchen,240.61,0,Vinyl plank flooring
+FCV,PLK,Bedroom3,244.88,0,Vinyl plank flooring
+FCV,PLK,Bedroom,144.57,0,Vinyl plank flooring
+FCV,PLK,Nook,144.57,0,Vinyl plank flooring
+FCV,PLK,Hallway2,130.9,0,Vinyl plank flooring
+FCV,PLK,Bedroom4,325.87,0,Vinyl plank flooring
+FCV,PLK,LivingRoom,408.7,0,Vinyl plank flooring
+FCV,PLK,Hallway,86.65,0,Vinyl plank flooring
+FCV,PLK,DiningRoom,314.14,0,Vinyl plank flooring
+FCW,AV,EntryFoyer,204.25,0,Oak flooring - #1 common - no finish
+FCW,AV,Bath,82.94,0,Oak flooring - #1 common - no finish
+FCW,AV,Bath3,164.51,0,Oak flooring - #1 common - no finish
+FCW,AV,Bath2,51.96,0,Oak flooring - #1 common - no finish
+FCW,AV,LaundryRoom,164.51,0,Oak flooring - #1 common - no finish
+FCW,AV,UtilityRoom,245.84,0,Oak flooring - #1 common - no finish
+FCW,AV,WalkInCloset,245.84,0,Oak flooring - #1 common - no finish
+FCW,AV,FamilyRoom,443.87,0,Oak flooring - #1 common - no finish
+FCW,AV,Bedroom2,345.95,0,Oak flooring - #1 common - no finish
+FCW,AV,Kitchen,339.96,0,Oak flooring - #1 common - no finish
+FCW,AV,Bedroom3,345.95,0,Oak flooring - #1 common - no finish
+FCW,AV,Bedroom,204.25,0,Oak flooring - #1 common - no finish
+FCW,AV,Nook,204.25,0,Oak flooring - #1 common - no finish
+FCW,AV,Hallway2,185.02,0,Oak flooring - #1 common - no finish
+FCW,AV,Bedroom4,460.45,0,Oak flooring - #1 common - no finish
+FCW,AV,LivingRoom,577.51,0,Oak flooring - #1 common - no finish
+FCW,AV,Hallway,122.47,0,Oak flooring - #1 common - no finish
+FCW,AV,DiningRoom,443.87,0,Oak flooring - #1 common - no finish
+FCW,FIN,EntryFoyer,90.85,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bath,36.88,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bath3,73.18,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bath2,23.12,0,Sand & finish wood floor (natural finish)
+FCW,FIN,LaundryRoom,73.18,0,Sand & finish wood floor (natural finish)
+FCW,FIN,UtilityRoom,109.34,0,Sand & finish wood floor (natural finish)
+FCW,FIN,WalkInCloset,109.34,0,Sand & finish wood floor (natural finish)
+FCW,FIN,FamilyRoom,197.42,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bedroom2,153.87,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Kitchen,151.21,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bedroom3,153.87,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bedroom,90.85,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Nook,90.85,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Hallway2,82.3,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bedroom4,204.81,0,Sand & finish wood floor (natural finish)
+FCW,FIN,LivingRoom,256.88,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Hallway,54.47,0,Sand & finish wood floor (natural finish)
+FCW,FIN,DiningRoom,197.42,0,Sand & finish wood floor (natural finish)
+FCW,LAM,EntryFoyer,132.76,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bath,53.91,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bath3,106.93,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bath2,33.76,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,LaundryRoom,106.93,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,UtilityRoom,159.8,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,WalkInCloset,159.8,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,FamilyRoom,288.51,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bedroom2,224.87,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Kitchen,220.96,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bedroom3,224.87,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bedroom,132.76,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Nook,132.76,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Hallway2,120.25,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bedroom4,299.29,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,LivingRoom,375.37,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Hallway,79.59,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,DiningRoom,288.51,0,Snaplock Laminate - simulated wood flooring
+FNC,B3+,EntryFoyer,279.82,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bath,183.25,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bath3,252.56,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bath2,141.13,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,LaundryRoom,252.56,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,UtilityRoom,308.27,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,WalkInCloset,308.27,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,FamilyRoom,420.95,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bedroom2,365.24,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Kitchen,365.24,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bedroom3,365.24,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bedroom,279.82,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Nook,279.82,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Hallway2,294.68,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bedroom4,420.95,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,LivingRoom,477.84,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Hallway,266.15,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,DiningRoom,420.95,0,"Baseboard - 3 1/4"" stain grade"
+FNC,CLROD,general,26.5,0,Closet rod
+FNC,CLROD,WalkInCloset,139.1,0,Closet rod
+FNC,CLROD,Bedroom2,33.12,0,Closet rod
+FNC,CLROD,Bedroom3,33.12,0,Closet rod
+FNC,CLROD,Bedroom,33.12,0,Closet rod
+FNC,CLROD,Bedroom4,33.12,0,Closet rod
+FNC,HR+,general,365.59,0,Handrail - detailed profile - softwood - wall mounted
+FNC,SH16,general,315.36,0,"Shelving - 16"" - in place"
+FNC,SH16,general,63.08,0,"Shelving - 16"" - in place"
+FNC,SH16,Bath,189.22,0,"Shelving - 16"" - in place"
+FNC,SH16,Bath3,189.22,0,"Shelving - 16"" - in place"
+FNC,SH16,Bath2,189.22,0,"Shelving - 16"" - in place"
+FNC,SH16,UtilityRoom,331.12,0,"Shelving - 16"" - in place"
+FNC,SH16,WalkInCloset,331.12,0,"Shelving - 16"" - in place"
+FNC,SH16,Bedroom2,78.84,0,"Shelving - 16"" - in place"
+FNC,SH16,Bedroom3,78.84,0,"Shelving - 16"" - in place"
+FNC,SH16,Bedroom,78.84,0,"Shelving - 16"" - in place"
+FNC,SH16,Bedroom4,78.84,0,"Shelving - 16"" - in place"
+FNC,SILL+,general,944.52,0,Window sill - stain grade
+FNH,DBX,general_main,383.84,0,Door lockset & deadbolt - exterior
+FNH,DORH,general,59,0,Door knob - interior
+FNH,DORH,general,59,0,Door knob - interior
+FNH,DORH,Bath,59,0,Door knob - interior
+FNH,DORH,Bath3,59,0,Door knob - interior
+FNH,DORH,Bath2,59,0,Door knob - interior
+FNH,DORH,LaundryRoom,59,0,Door knob - interior
+FNH,DORH,UtilityRoom,59,0,Door knob - interior
+FNH,DORH,WalkInCloset,59,0,Door knob - interior
+FNH,DORH,Bedroom2,59,0,Door knob - interior
+FNH,DORH,Bedroom3,59,0,Door knob - interior
+FNH,DORH,Bedroom,59,0,Door knob - interior
+FNH,DORH,Bedroom4,59,0,Door knob - interior
+FNH,SROD,Bath,42.38,0,Shower curtain rod
+FNH,TBAR,Bath,44.87,0,Towel bar
+FNH,TBAR,Bath3,44.87,0,Towel bar
+FNH,TBAR,Bath2,44.87,0,Towel bar
+FNH,TP,Bath,40.78,0,Toilet paper holder
+FNH,TP,Bath3,40.78,0,Toilet paper holder
+FNH,TP,Bath2,40.78,0,Toilet paper holder
+FNH,TR,Bath,44.92,0,Towel ring
+FNH,TR,Bath3,44.92,0,Towel ring
+FNH,TR,Bath2,44.92,0,Towel ring
+HVC,AC>,mech_single,5644.2,0,Central air conditioning system - 4 ton - up to 13 SEER
+HVC,BVENT,Bath,160.8,0,Bathroom ventilation fan
+HVC,BVENT,Bath3,160.8,0,Bathroom ventilation fan
+HVC,BVENT,Bath2,160.8,0,Bathroom ventilation fan
+HVC,DCT,mech_single,7027.78,0,Ductwork system - hot or cold air - 1200 to 1599 SF home
+HVC,DCTFN3,Bath,95.76,0,"Ductwork - flexible - non-insulated - 3"" round"
+HVC,DCTFN3,Bath3,95.76,0,"Ductwork - flexible - non-insulated - 3"" round"
+HVC,DCTFN3,Bath2,95.76,0,"Ductwork - flexible - non-insulated - 3"" round"
+HVC,DRNL,mech_single,98.14,0,Condensate drain line
+HVC,DVENT,general,101.74,0,Clothes dryer vent - installed
+HVC,FRFAH>,mech_single,5274.98,0,"Furnace - forced air - high efficiency - 100,000 BTU"
+HVC,TS+,general,205.57,0,Thermostat - High grade
+INS,BTF10,general,8850,0,"Batt insulation - 10"" - R30 - paper / foil faced"
+INS,BTF6,general,5486.35,0,"Batt insulation - 6"" - R19 - paper / foil faced"
+LIT,AV,EntryFoyer,85.6,0,Light fixture
+LIT,AV,FamilyRoom,85.6,0,Light fixture
+LIT,AV,Bedroom2,85.6,0,Light fixture
+LIT,AV,Bedroom3,85.6,0,Light fixture
+LIT,AV,Bedroom,85.6,0,Light fixture
+LIT,AV,Hallway2,85.6,0,Light fixture
+LIT,AV,Bedroom4,85.6,0,Light fixture
+LIT,AV,LivingRoom,85.6,0,Light fixture
+LIT,AV,Hallway,85.6,0,Light fixture
+LIT,AV,DiningRoom,85.6,0,Light fixture
+LIT,AV-,LaundryRoom,67.07,0,Light fixture - Standard grade
+LIT,AV-,UtilityRoom,67.07,0,Light fixture - Standard grade
+LIT,AV-,WalkInCloset,67.07,0,Light fixture - Standard grade
+LIT,AV-,Nook,67.07,0,Light fixture - Standard grade
+LIT,BAR3+,Bath3,199.36,0,Light bar - 3 lights - High grade
+LIT,BAR4,Bath2,120.15,0,Light bar - 4 lights
+LIT,BAR5,Bath,139.44,0,Light bar - 5 lights
+LIT,FL2-4+,general,151.87,0,Fluorescent - two tube - 4' - fixture w/lens
+LIT,FL2-4+,Kitchen,151.87,0,Fluorescent - two tube - 4' - fixture w/lens
+LIT,X,general_main,338.51,0,Exterior light fixture
+LIT,X-,general_main,91.59,0,Exterior light fixture - Standard grade
+LIT,X-,general_main,91.59,0,Exterior light fixture - Standard grade
+MSD,AV,Bath,137.02,0,"Mirror - 1/4"" plate glass"
+MSD,AV,Bath3,137.02,0,"Mirror - 1/4"" plate glass"
+MSD,AV,Bath2,137.02,0,"Mirror - 1/4"" plate glass"
+MSD,SDOR,Bath3,711.98,0,Shower door
+PLM,FAU,Kitchen,333.57,0,Sink faucet - Kitchen
+PLM,FAUBA,Bath,284.37,0,Sink faucet - Bathroom
+PLM,FAUBA,Bath3,284.37,0,Sink faucet - Bathroom
+PLM,FAUBA,Bath3,284.37,0,Sink faucet - Bathroom
+PLM,FAUBA,Bath2,284.37,0,Sink faucet - Bathroom
+PLM,RFIXP,Bath,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath2,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath2,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath2,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath2,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Kitchen,791.23,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RGHFIX,LaundryRoom,892.28,0,Rough-in plumbing - per fixture
+PLM,SNK,Bath,374.41,0,Sink - single
+PLM,SNK,Bath3,374.41,0,Sink - single
+PLM,SNK,Bath2,374.41,0,Sink - single
+PLM,SNKD,Kitchen,507.47,0,Sink - double basin
+PLM,SWRPAN,Bath3,222.33,0,Shower pan
+PLM,TLT,Bath,663.12,0,Toilet
+PLM,TLT,Bath3,663.12,0,Toilet
+PLM,TLT,Bath2,663.12,0,Toilet
+PLM,TLTS,Bath,71.85,0,Toilet seat
+PLM,TLTS,Bath3,71.85,0,Toilet seat
+PLM,TLTS,Bath2,71.85,0,Toilet seat
+PLM,TSFAU,Bath,425.78,0,Tub/shower faucet
+PLM,TSFAU,Bath3,425.78,0,Tub/shower faucet
+PLM,TUB,Bath,1170.43,0,Bathtub
+PLM,TUB,Bath3,1170.43,0,Bathtub
+PLM,WBOX,LaundryRoom,295.58,0,Washing machine outlet box with valves
+PLM,WH50,mech_single,1836.39,0,Water heater - 50 gallon - Gas - 6 yr
+PNT,BF,Bedroom2,200.32,0,Paint bifold door set - slab only - 2 coats (per side)
+PNT,BF,Bedroom3,200.32,0,Paint bifold door set - slab only - 2 coats (per side)
+PNT,BF,Bedroom,200.32,0,Paint bifold door set - slab only - 2 coats (per side)
+PNT,BF,Bedroom4,200.32,0,Paint bifold door set - slab only - 2 coats (per side)
+PNT,BS,EntryFoyer,118.89,0,Stain & finish baseboard
+PNT,BS,Bath,77.86,0,Stain & finish baseboard
+PNT,BS,Bath3,107.3,0,Stain & finish baseboard
+PNT,BS,Bath2,59.97,0,Stain & finish baseboard
+PNT,BS,LaundryRoom,107.3,0,Stain & finish baseboard
+PNT,BS,UtilityRoom,130.99,0,Stain & finish baseboard
+PNT,BS,WalkInCloset,130.99,0,Stain & finish baseboard
+PNT,BS,FamilyRoom,178.84,0,Stain & finish baseboard
+PNT,BS,Bedroom2,155.18,0,Stain & finish baseboard
+PNT,BS,Kitchen,155.18,0,Stain & finish baseboard
+PNT,BS,Bedroom3,155.18,0,Stain & finish baseboard
+PNT,BS,Bedroom,118.89,0,Stain & finish baseboard
+PNT,BS,Nook,118.89,0,Stain & finish baseboard
+PNT,BS,Hallway2,125.19,0,Stain & finish baseboard
+PNT,BS,Bedroom4,178.84,0,Stain & finish baseboard
+PNT,BS,LivingRoom,203.03,0,Stain & finish baseboard
+PNT,BS,Hallway,113.07,0,Stain & finish baseboard
+PNT,BS,DiningRoom,178.84,0,Stain & finish baseboard
+PNT,DOR,general,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,general,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bath,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bath3,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bath2,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,LaundryRoom,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,UtilityRoom,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,WalkInCloset,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bedroom2,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bedroom3,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bedroom,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bedroom4,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DORT,general,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,general,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bath,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bath3,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bath2,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,LaundryRoom,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,UtilityRoom,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,WalkInCloset,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bedroom2,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bedroom3,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bedroom,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bedroom4,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,HRS,general,63.91,0,Stain & finish handrail - wall mounted
+PNT,P,EntryFoyer,120.24,0,Paint the surface area - one coat
+PNT,P,Bath,48.84,0,Paint the surface area - one coat
+PNT,P,Bath3,96.83,0,Paint the surface area - one coat
+PNT,P,Bath2,30.59,0,Paint the surface area - one coat
+PNT,P,LaundryRoom,96.83,0,Paint the surface area - one coat
+PNT,P,UtilityRoom,144.71,0,Paint the surface area - one coat
+PNT,P,WalkInCloset,144.71,0,Paint the surface area - one coat
+PNT,P,FamilyRoom,369.43,0,Paint the surface area - one coat
+PNT,P,Bedroom2,203.63,0,Paint the surface area - one coat
+PNT,P,Kitchen,200.11,0,Paint the surface area - one coat
+PNT,P,Bedroom3,203.63,0,Paint the surface area - one coat
+PNT,P,Bedroom,120.24,0,Paint the surface area - one coat
+PNT,P,Nook,120.24,0,Paint the surface area - one coat
+PNT,P,Hallway2,108.86,0,Paint the surface area - one coat
+PNT,P,Bedroom4,270.99,0,Paint the surface area - one coat
+PNT,P,LivingRoom,480.58,0,Paint the surface area - one coat
+PNT,P,Hallway,72.04,0,Paint the surface area - one coat
+PNT,P,DiningRoom,261.26,0,Paint the surface area - one coat
+PNT,S,EntryFoyer,113.86,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bath,46.24,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bath3,91.69,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bath2,28.96,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,LaundryRoom,91.69,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,UtilityRoom,137.03,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,WalkInCloset,137.03,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,FamilyRoom,349.8,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bedroom2,192.82,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Kitchen,189.48,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bedroom3,192.82,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bedroom,113.86,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Nook,113.86,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Hallway2,103.08,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bedroom4,256.6,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,LivingRoom,455.06,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Hallway,68.23,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,DiningRoom,247.4,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,SHW,general,186,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,general,37.2,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bath,111.6,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bath3,111.6,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bath2,111.6,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,UtilityRoom,195.31,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,WalkInCloset,195.31,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bedroom2,46.51,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bedroom3,46.51,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bedroom,46.51,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bedroom4,46.51,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SILL,general,899.64,0,Seal & paint window sill
+PNT,SP,general,202.18,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,general,202.18,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,EntryFoyer,585.79,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bath,383.61,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bath3,528.76,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bath2,295.48,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,LaundryRoom,528.76,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,UtilityRoom,645.4,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,WalkInCloset,645.4,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,FamilyRoom,881.27,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bedroom2,764.63,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Kitchen,764.63,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bedroom3,764.63,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bedroom,585.79,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Nook,585.79,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Hallway2,616.89,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bedroom4,881.27,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,LivingRoom,1000.53,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Hallway,557.29,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,DiningRoom,881.27,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+TIL,BMUD,Bath,817.08,0,Mortar bed for tile
+TIL,BMUD,Bath3,1173.62,0,Mortar bed for tile
+TIL,SWR,Bath3,3402.52,0,Tile shower - 61 to 100 SF
+TIL,TUB,Bath,1786.28,0,Tile tub surround - up to 60 SF
+WDP,6V,general_main,1391.12,0,6-0 6-8 vinyl sliding patio door
diff --git a/cancurve/dev_test_data/case3/readme.txt b/cancurve/dev_test_data/case3/readme.txt
new file mode 100644
index 0000000..3dd0500
--- /dev/null
+++ b/cancurve/dev_test_data/case3/readme.txt
@@ -0,0 +1,3 @@
+2024-05-09: case provided by Heather
+
+https://mail.google.com/mail/u/2/#inbox/FMfcgzGxTFXMrNkBZRjsBTvKsXcSlZss
\ No newline at end of file
diff --git a/cancurve/dev_test_data/readme.md b/cancurve/dev_test_data/readme.md
new file mode 100644
index 0000000..2e3708f
--- /dev/null
+++ b/cancurve/dev_test_data/readme.md
@@ -0,0 +1,3 @@
+# Temporary Plugin UI Testing Data
+copied from `l:\09_REPOS\04_TOOLS\CanCurve\tests\data\bldgs\`
+This is needed by the **test case** widget on teh Welcome tab
\ No newline at end of file
diff --git a/cancurve/hp/basic.py b/cancurve/hp/basic.py
index 5e7f92d..57ad8a2 100644
--- a/cancurve/hp/basic.py
+++ b/cancurve/hp/basic.py
@@ -4,7 +4,7 @@
@author: cef
'''
-import os
+import os, logging
import pandas as pd
def view_web_df(df):
@@ -67,7 +67,7 @@ def convert_to_number(text):
text: The string to convert.
Returns:
- The converted number (int or float) if successful, None otherwise.
+ The converted number (int or float) if successful, original otherwise.
"""
try:
return int(text)
@@ -76,3 +76,43 @@ def convert_to_number(text):
return float(text)
except ValueError:
return text
+
+
+def convert_to_float(text):
+ try:
+ return float(text)
+ except ValueError:
+ return text
+
+def force_open_dir(folder_path_raw, logger=None): #force explorer to open a folder
+
+ if logger is None: logger= logging.getLogger()
+
+
+ if not os.path.exists(folder_path_raw):
+ logger.error('passed directory does not exist: \n %s'%folder_path_raw)
+ return False
+
+ import subprocess
+
+ #===========================================================================
+ # convert directory to raw string literal for windows
+ #===========================================================================
+ try:
+ #convert forward to backslashes
+ folder_path= folder_path_raw.replace('/', '\\')
+ except:
+ logger.error('failed during string conversion')
+ return False
+
+ try:
+
+ args = r'explorer "' + str(folder_path) + '"'
+ with subprocess.Popen(args) as p: #spawn process in explorer
+ pass
+
+ logger.info('forced open folder: \n %s'%folder_path)
+ return True
+ except:
+ logger.error('unable to open directory: \n %s'%dir)
+ return False
diff --git a/cancurve/hp/qt.py b/cancurve/hp/qt.py
index 0f6098a..77731da 100644
--- a/cancurve/hp/qt.py
+++ b/cancurve/hp/qt.py
@@ -230,10 +230,4 @@ def _change_tab(self, tabObjectName): #try to switch the tab on the gui
raise IOError(f'bad tabname \'{tabObjectName}\'')
self.logger.error(f'failed to change to {tabObjectName} tab w/ \n %s' % e)
-def _connect_open_file(self, browse_pushButton, filepath_lineEdit):
- """connect a default 'open file' dialog to the browse_button
- display the result in the lineEdit"""
-
-
-
- browse_pushButton.clicked.connect(handle_browse)
\ No newline at end of file
+
\ No newline at end of file
diff --git a/cancurve/metadata.txt b/cancurve/metadata.txt
index 828405a..86d2599 100644
--- a/cancurve/metadata.txt
+++ b/cancurve/metadata.txt
@@ -5,12 +5,12 @@
[general]
name=CanCurve
qgisMinimumVersion=3.34
-description=Creating depth-damage functions
-version=0.0
-author=NRCan
+description=Free tools to create Canadian flood Depth-Damage Functions (DDF)
+version=0.0.1
+author=Seth Bryant
email=heather.mcgrath@canada.ca
-about=This tool is designed to create depth (stage) damage functions from user input.
+about = CanCurve is an open-source tool which can develop depth-damage (stage-damage) functions for use in flood loss estimation and flood risk assessments.
tracker=https://github.com/NRCan/CanCurve/issues
repository=https://github.com/NRCan/CanCurve
@@ -20,14 +20,14 @@ repository=https://github.com/NRCan/CanCurve
hasProcessingProvider=no
# Uncomment the following line and add your changelog:
-# changelog=
+changelog= v0.0.1 [2024-05] initial working release (un tested)
# Tags are comma separated with spaces allowed
-tags=flood
+tags=flood risk, flood, model, Canada
homepage=https://github.com/NRCan/CanCurve
category=Plugins
-icon=icon.png
+icon=img/icon.png
# experimental flag
experimental=True
diff --git a/dev_tools/cc_compile.bat b/dev_tools/plug_compile.bat
similarity index 100%
rename from dev_tools/cc_compile.bat
rename to dev_tools/plug_compile.bat
diff --git a/dev_tools/cc_deploy.bat b/dev_tools/plug_deploy.bat
similarity index 100%
rename from dev_tools/cc_deploy.bat
rename to dev_tools/plug_deploy.bat
diff --git a/dev_tools/cc_zip.bat b/dev_tools/plug_zip.bat
similarity index 99%
rename from dev_tools/cc_zip.bat
rename to dev_tools/plug_zip.bat
index 66c16e5..4ef2f04 100644
--- a/dev_tools/cc_zip.bat
+++ b/dev_tools/plug_zip.bat
@@ -9,10 +9,7 @@ SET PATH=C:\ProgramData\chocolatey\bin;%PATH%
:: change to plugin directory
cd %SRC_DIR%
-
-
-
-
+
REM ** 1) Remove __pycache__ directories **
for /d /r %PLUGIN_DIR% %%d in (__pycache__) do rd /s /q "%%d"
diff --git a/tests/bldgs/conftest.py b/tests/bldgs/conftest.py
index 32b226c..4dcda6e 100644
--- a/tests/bldgs/conftest.py
+++ b/tests/bldgs/conftest.py
@@ -5,18 +5,18 @@
'''
import pytest, os, shutil, random
-from cancurve.hp.basic import find_single_file_by_extension, convert_to_number
+from cancurve.hp.basic import find_single_file_by_extension, convert_to_number, convert_to_float
from cancurve.bldgs.parameters_ui import building_details_options_d
from cancurve.bldgs.parameters import bldg_meta_rqmt_df
from cancurve.bldgs.dialog_test_scripts import (
- test_data_dir_master, fixed_costs_master_d)
+ fixed_costs_master_d)
#===============================================================================
# data
#===============================================================================
-#from tests.conftest import test_data_dir_master as parent_tdata_dir
-
+from tests.conftest import test_data_dir_master as parent_tdata_dir
+test_data_dir_master = os.path.join(parent_tdata_dir, 'bldgs')
#===============================================================================
# fixtrues--------
#===============================================================================
@@ -52,7 +52,7 @@ def bldg_meta_d(testCase):
d = bldg_meta_rqmt_df.loc[:, ['varName_core', testCase]].dropna().set_index('varName_core').iloc[:, 0].to_dict()
- return {k:convert_to_number(v) for k,v in d.items()}
+ return {k:convert_to_float(v) for k,v in d.items()}
#===============================================================================
# @pytest.fixture(scope='function')
diff --git a/tests/bldgs/test_core.py b/tests/bldgs/test_core.py
index c623558..3d9bd36 100644
--- a/tests/bldgs/test_core.py
+++ b/tests/bldgs/test_core.py
@@ -61,6 +61,7 @@ def copy_sqlite(proj_db_fp, testCase, destinationPhase, write=False):
@pytest.mark.parametrize('testCase',[
'case1',
'case2',
+ pytest.param('case3', marks=pytest.mark.xfail(raises=KeyError, reason="storey data mismatch")),
], indirect=False)
#===============================================================================
# @pytest.mark.parametrize('fixed_costs_d',[
diff --git a/tests/bldgs/test_dialog.py b/tests/bldgs/test_dialog.py
index 99bc649..4820db4 100644
--- a/tests/bldgs/test_dialog.py
+++ b/tests/bldgs/test_dialog.py
@@ -45,7 +45,7 @@
#===============================================================================
-# fixtures------
+# FIXTURES------
#===============================================================================
#===============================================================================
@@ -161,7 +161,7 @@ def set_projdb(dialog, proj_db_fp):
#===============================================================================
-# tests------
+# TESTS------
#===============================================================================
def test_parameters():
@@ -173,8 +173,8 @@ def test_parameters():
def test_init(dialog,):
- """manual inspection only"""
#===========================================================================
+ # """manual inspection only"""
# dialog.show()
# QApp = QApplication(sys.argv) #initlize a QT appliaction (inplace of Qgis) to manually inspect
# sys.exit(QApp.exec_()) #wrap
@@ -311,18 +311,20 @@ def test_radioButton_tab4actions_runControl(dialog):
-def test_pushButton_tab4actions_browse(dialog):
- enable_widget_and_parents(dialog.pushButton_tab4actions_browse)
-
-
- # Mock QFileDialog.getOpenFileName to return a predetermined filename
- with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName', return_value=('test_filename', '')):
- # Simulate a mouse click on the browse button
- print('clicking browse from within mock')
- QTest.mouseClick(dialog.pushButton_tab4actions_browse, Qt.LeftButton)
-
- # Check that the line edit's text is the expected filename
- assert dialog.lineEdit_tab4actions_projdb.text() == 'test_filename'
+#===============================================================================
+# def test_pushButton_tab4actions_browse(dialog):
+# enable_widget_and_parents(dialog.pushButton_tab4actions_browse)
+#
+#
+# # Mock QFileDialog.getOpenFileName to return a predetermined filename
+# with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName', return_value=('test_filename', '')):
+# # Simulate a mouse click on the browse button
+# print('clicking browse from within mock')
+# QTest.mouseClick(dialog.pushButton_tab4actions_browse, Qt.LeftButton)
+#
+# # Check that the line edit's text is the expected filename
+# assert dialog.lineEdit_tab4actions_projdb.text() == 'test_filename'
+#===============================================================================
@@ -347,6 +349,36 @@ def test_pushButton_tab4actions_read(dialog, set_projdb):
# QApp = QApplication(sys.argv) #initlize a QT appliaction (inplace of Qgis) to manually inspect
# sys.exit(QApp.exec_()) #wrap
#===========================================================================
+
+@pytest.mark.dev
+@pytest.mark.parametrize('buttonName, lineName, QFileDialogTypeName',[
+ ('pushButton_wd','lineEdit_wdir','getExistingDirectory'),
+ ('pushButton_tab3dataInput_cifp','lineEdit_tab3dataInput_cifp', 'getOpenFileName'),
+ ('pushButton_tab3dataInput_drfFp','lineEdit_tab3dataInput_drfFp', 'getOpenFileName'),
+ ('pushButton_tab4actions_browse', 'lineEdit_tab4actions_projdb', 'getOpenFileName'),
+ ])
+def test_file_buttons(dialog, buttonName, lineName, QFileDialogTypeName):
+
+ #retrieve button widget
+ assert hasattr(dialog, buttonName), buttonName
+ w = getattr(dialog, buttonName)
+ enable_widget_and_parents(w)
+
+ #lineEdit widget
+ assert hasattr(dialog, lineName)
+ lineWidget = getattr(dialog, lineName)
+
+ #PyQt5.QtWidgets.QFileDialog.
+
+ with patch('PyQt5.QtWidgets.QFileDialog.' + QFileDialogTypeName, return_value=('test_string', '')):
+ # Simulate a mouse click on the browse button
+ print(f'clicking {buttonName} from within mock')
+ QTest.mouseClick(w, Qt.LeftButton)
+
+ # Check that the line edit's text is the expected filename
+ assert lineWidget.text() == 'test_string', f'test string failed to set on {lineName}'
+
+
#===============================================================================
# Dialog Action tests--------
@@ -413,7 +445,7 @@ def test_action_tab4actions_step1(dialog,
-@pytest.mark.dev
+
#@patch('matplotlib.pyplot.show') #breaks enable_widget for some reason...
@pytest.mark.parametrize('testCase', ['case1'])
@pytest.mark.parametrize('button, testPhase, expected_tables', [
diff --git a/tests/bldgs/test_plots.py b/tests/bldgs/test_plots.py
index 96c12f2..90605b6 100644
--- a/tests/bldgs/test_plots.py
+++ b/tests/bldgs/test_plots.py
@@ -50,7 +50,7 @@ def action_result(testCase, testPhase):
"""intelligently retrieve result for this case and phase from test_core"""
tdata_dir = os.path.join(test_data_dir_master, testCase, testPhase)
- assert os.path.exists(tdata_dir)
+ assert os.path.exists(tdata_dir), tdata_dir
fp = find_single_file_by_extension(tdata_dir, '.pkl')
@@ -67,7 +67,7 @@ def action_result(testCase, testPhase):
#===============================================================================
# tests---------
#===============================================================================
-#
+@pytest.mark.dev
@pytest.mark.parametrize('testPhase',['c00'], indirect=False)
@pytest.mark.parametrize('testCase',[
'case1',
diff --git a/tests/data/bldgs/case3/C_1-L-NoB-ST_ABCA.csv b/tests/data/bldgs/case3/C_1-L-NoB-ST_ABCA.csv
new file mode 100644
index 0000000..8447d4c
--- /dev/null
+++ b/tests/data/bldgs/case3/C_1-L-NoB-ST_ABCA.csv
@@ -0,0 +1,473 @@
+cat,sel,group_code,rcv,story,desc
+DOR,AV,Bath,493.59,0,Interior door unit
+DOR,AV,Bath3,493.59,0,Interior door unit
+DOR,AV,Bath2,493.59,0,Interior door unit
+DOR,AV,LaundryRoom,493.59,0,Interior door unit
+DOR,AV,WalkInCloset,493.59,0,Interior door unit
+DOR,AV,Bedroom,493.59,0,Interior door unit
+DRY,1/2+,Bath,178.52,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bath3,1348.04,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bath3,353.9,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bath2,753.32,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bath2,111.81,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,LaundryRoom,1348.04,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,LaundryRoom,353.9,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,UtilityRoom,1645.4,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,UtilityRoom,528.91,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,WalkInCloset,1645.4,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,WalkInCloset,528.91,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,FamilyRoom,2246.71,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,FamilyRoom,1350.16,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom2,1949.35,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom2,744.24,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Kitchen,1949.35,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Kitchen,731.36,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom3,1949.35,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom3,744.24,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom,1493.39,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom,439.45,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Nook,1493.39,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Nook,439.45,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Hallway2,1572.69,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Hallway2,397.88,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom4,2246.71,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Bedroom4,990.46,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,LivingRoom,2550.71,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,LivingRoom,1756.45,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Hallway,1420.75,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,Hallway,263.31,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,DiningRoom,2246.71,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+DRY,1/2+,DiningRoom,954.87,0,"1/2"" drywall - hung, taped, heavy texture, ready for paint"
+ELE,110,Kitchen,105.88,0,"110 volt copper wiring run, box and outlet"
+ELE,BPNL200,mech_single,2026.63,0,Breaker panel - 200 amp
+ELE,GFI,general_main,44.8,0,Ground fault interrupter (GFI) outlet
+ELE,GFI,Bath,44.8,0,Ground fault interrupter (GFI) outlet
+ELE,GFI,Bath3,89.62,0,Ground fault interrupter (GFI) outlet
+ELE,GFI,Bath2,44.8,0,Ground fault interrupter (GFI) outlet
+ELE,GFI,LaundryRoom,44.8,0,Ground fault interrupter (GFI) outlet
+ELE,GFI,Kitchen,89.62,0,Ground fault interrupter (GFI) outlet
+ELE,GFI,general_main,44.8,0,Ground fault interrupter (GFI) outlet
+ELE,LV,general,58.08,0,Phone / low voltage copper wiring
+ELE,LV,general,58.08,0,Phone / low voltage copper wiring
+ELE,OL,general,49.54,0,"Phone, TV, or speaker outlet"
+ELE,OL,general,99.08,0,"Phone, TV, or speaker outlet"
+ELE,OL220,LaundryRoom,45.46,0,220 volt outlet
+ELE,OL220,Kitchen,45.46,0,220 volt outlet
+ELE,OS,EntryFoyer,38.74,0,Outlet
+ELE,OS,general_main,19.36,0,Outlet
+ELE,OS,Bath,38.74,0,Outlet
+ELE,OS,Bath3,38.74,0,Outlet
+ELE,OS,Bath2,38.74,0,Outlet
+ELE,OS,LaundryRoom,19.36,0,Outlet
+ELE,OS,UtilityRoom,116.2,0,Outlet
+ELE,OS,WalkInCloset,38.74,0,Outlet
+ELE,OS,FamilyRoom,154.94,0,Outlet
+ELE,OS,Bedroom2,116.2,0,Outlet
+ELE,OS,Kitchen,77.48,0,Outlet
+ELE,OS,Kitchen,38.74,0,Outlet
+ELE,OS,Bedroom3,116.2,0,Outlet
+ELE,OS,Bedroom,116.2,0,Outlet
+ELE,OS,Nook,77.48,0,Outlet
+ELE,OS,Hallway2,38.74,0,Outlet
+ELE,OS,Bedroom4,116.2,0,Outlet
+ELE,OS,LivingRoom,154.94,0,Outlet
+ELE,OS,general_main,19.36,0,Outlet
+ELE,OS,Hallway,38.74,0,Outlet
+ELE,OS,DiningRoom,96.84,0,Outlet
+ELE,REWIRE,general,12720,0,Rewire\wire - avg. residence - boxes & wiring
+ELE,SMOKE,general,80.96,0,Smoke detector
+ELE,SMOKE,Bedroom2,80.96,0,Smoke detector
+ELE,SMOKE,Bedroom3,80.96,0,Smoke detector
+ELE,SMOKE,Bedroom,80.96,0,Smoke detector
+ELE,SMOKE,Hallway2,80.96,0,Smoke detector
+ELE,SMOKE,Bedroom4,80.96,0,Smoke detector
+ELE,SMOKE,Hallway,80.96,0,Smoke detector
+ELE,XOS,general_main,56.68,0,Exterior outlet or switch
+FCC,AV,EntryFoyer,115.99,0,Carpet
+FCC,AV,Bath,47.11,0,Carpet
+FCC,AV,Bath3,93.39,0,Carpet
+FCC,AV,Bath2,29.52,0,Carpet
+FCC,AV,LaundryRoom,93.39,0,Carpet
+FCC,AV,UtilityRoom,139.65,0,Carpet
+FCC,AV,WalkInCloset,139.65,0,Carpet
+FCC,AV,FamilyRoom,252.03,0,Carpet
+FCC,AV,Bedroom2,196.46,0,Carpet
+FCC,AV,Kitchen,193.06,0,Carpet
+FCC,AV,Bedroom3,196.46,0,Carpet
+FCC,AV,Bedroom,115.99,0,Carpet
+FCC,AV,Nook,115.99,0,Carpet
+FCC,AV,Hallway2,105,0,Carpet
+FCC,AV,Bedroom4,261.48,0,Carpet
+FCC,AV,LivingRoom,327.92,0,Carpet
+FCC,AV,Hallway,69.5,0,Carpet
+FCC,AV,DiningRoom,252.03,0,Carpet
+FCC,PAD,EntryFoyer,16.89,0,Carpet pad
+FCC,PAD,Bath,6.85,0,Carpet pad
+FCC,PAD,Bath3,13.59,0,Carpet pad
+FCC,PAD,Bath2,4.3,0,Carpet pad
+FCC,PAD,LaundryRoom,13.59,0,Carpet pad
+FCC,PAD,UtilityRoom,20.32,0,Carpet pad
+FCC,PAD,WalkInCloset,20.32,0,Carpet pad
+FCC,PAD,FamilyRoom,36.69,0,Carpet pad
+FCC,PAD,Bedroom2,28.59,0,Carpet pad
+FCC,PAD,Kitchen,28.1,0,Carpet pad
+FCC,PAD,Bedroom3,28.59,0,Carpet pad
+FCC,PAD,Bedroom,16.89,0,Carpet pad
+FCC,PAD,Nook,16.89,0,Carpet pad
+FCC,PAD,Hallway2,15.28,0,Carpet pad
+FCC,PAD,Bedroom4,38.06,0,Carpet pad
+FCC,PAD,LivingRoom,47.74,0,Carpet pad
+FCC,PAD,Hallway,10.11,0,Carpet pad
+FCC,PAD,DiningRoom,36.69,0,Carpet pad
+FCV,AV,EntryFoyer,119.16,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bath,48.38,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bath3,95.97,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bath2,30.34,0,Vinyl floor covering (sheet goods)
+FCV,AV,LaundryRoom,95.97,0,Vinyl floor covering (sheet goods)
+FCV,AV,UtilityRoom,143.48,0,Vinyl floor covering (sheet goods)
+FCV,AV,WalkInCloset,143.48,0,Vinyl floor covering (sheet goods)
+FCV,AV,FamilyRoom,258.95,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bedroom2,201.85,0,Vinyl floor covering (sheet goods)
+FCV,AV,Kitchen,198.35,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bedroom3,201.85,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bedroom,119.16,0,Vinyl floor covering (sheet goods)
+FCV,AV,Nook,119.16,0,Vinyl floor covering (sheet goods)
+FCV,AV,Hallway2,107.88,0,Vinyl floor covering (sheet goods)
+FCV,AV,Bedroom4,268.65,0,Vinyl floor covering (sheet goods)
+FCV,AV,LivingRoom,336.92,0,Vinyl floor covering (sheet goods)
+FCV,AV,Hallway,71.41,0,Vinyl floor covering (sheet goods)
+FCV,AV,DiningRoom,258.95,0,Vinyl floor covering (sheet goods)
+FCV,PLK,EntryFoyer,144.57,0,Vinyl plank flooring
+FCV,PLK,Bath,58.69,0,Vinyl plank flooring
+FCV,PLK,Bath3,116.47,0,Vinyl plank flooring
+FCV,PLK,Bath2,36.81,0,Vinyl plank flooring
+FCV,PLK,LaundryRoom,116.47,0,Vinyl plank flooring
+FCV,PLK,UtilityRoom,173.99,0,Vinyl plank flooring
+FCV,PLK,WalkInCloset,173.99,0,Vinyl plank flooring
+FCV,PLK,FamilyRoom,314.14,0,Vinyl plank flooring
+FCV,PLK,Bedroom2,244.88,0,Vinyl plank flooring
+FCV,PLK,Kitchen,240.61,0,Vinyl plank flooring
+FCV,PLK,Bedroom3,244.88,0,Vinyl plank flooring
+FCV,PLK,Bedroom,144.57,0,Vinyl plank flooring
+FCV,PLK,Nook,144.57,0,Vinyl plank flooring
+FCV,PLK,Hallway2,130.9,0,Vinyl plank flooring
+FCV,PLK,Bedroom4,325.87,0,Vinyl plank flooring
+FCV,PLK,LivingRoom,408.7,0,Vinyl plank flooring
+FCV,PLK,Hallway,86.65,0,Vinyl plank flooring
+FCV,PLK,DiningRoom,314.14,0,Vinyl plank flooring
+FCW,AV,EntryFoyer,204.25,0,Oak flooring - #1 common - no finish
+FCW,AV,Bath,82.94,0,Oak flooring - #1 common - no finish
+FCW,AV,Bath3,164.51,0,Oak flooring - #1 common - no finish
+FCW,AV,Bath2,51.96,0,Oak flooring - #1 common - no finish
+FCW,AV,LaundryRoom,164.51,0,Oak flooring - #1 common - no finish
+FCW,AV,UtilityRoom,245.84,0,Oak flooring - #1 common - no finish
+FCW,AV,WalkInCloset,245.84,0,Oak flooring - #1 common - no finish
+FCW,AV,FamilyRoom,443.87,0,Oak flooring - #1 common - no finish
+FCW,AV,Bedroom2,345.95,0,Oak flooring - #1 common - no finish
+FCW,AV,Kitchen,339.96,0,Oak flooring - #1 common - no finish
+FCW,AV,Bedroom3,345.95,0,Oak flooring - #1 common - no finish
+FCW,AV,Bedroom,204.25,0,Oak flooring - #1 common - no finish
+FCW,AV,Nook,204.25,0,Oak flooring - #1 common - no finish
+FCW,AV,Hallway2,185.02,0,Oak flooring - #1 common - no finish
+FCW,AV,Bedroom4,460.45,0,Oak flooring - #1 common - no finish
+FCW,AV,LivingRoom,577.51,0,Oak flooring - #1 common - no finish
+FCW,AV,Hallway,122.47,0,Oak flooring - #1 common - no finish
+FCW,AV,DiningRoom,443.87,0,Oak flooring - #1 common - no finish
+FCW,FIN,EntryFoyer,90.85,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bath,36.88,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bath3,73.18,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bath2,23.12,0,Sand & finish wood floor (natural finish)
+FCW,FIN,LaundryRoom,73.18,0,Sand & finish wood floor (natural finish)
+FCW,FIN,UtilityRoom,109.34,0,Sand & finish wood floor (natural finish)
+FCW,FIN,WalkInCloset,109.34,0,Sand & finish wood floor (natural finish)
+FCW,FIN,FamilyRoom,197.42,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bedroom2,153.87,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Kitchen,151.21,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bedroom3,153.87,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bedroom,90.85,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Nook,90.85,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Hallway2,82.3,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Bedroom4,204.81,0,Sand & finish wood floor (natural finish)
+FCW,FIN,LivingRoom,256.88,0,Sand & finish wood floor (natural finish)
+FCW,FIN,Hallway,54.47,0,Sand & finish wood floor (natural finish)
+FCW,FIN,DiningRoom,197.42,0,Sand & finish wood floor (natural finish)
+FCW,LAM,EntryFoyer,132.76,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bath,53.91,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bath3,106.93,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bath2,33.76,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,LaundryRoom,106.93,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,UtilityRoom,159.8,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,WalkInCloset,159.8,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,FamilyRoom,288.51,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bedroom2,224.87,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Kitchen,220.96,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bedroom3,224.87,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bedroom,132.76,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Nook,132.76,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Hallway2,120.25,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Bedroom4,299.29,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,LivingRoom,375.37,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,Hallway,79.59,0,Snaplock Laminate - simulated wood flooring
+FCW,LAM,DiningRoom,288.51,0,Snaplock Laminate - simulated wood flooring
+FNC,B3+,EntryFoyer,279.82,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bath,183.25,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bath3,252.56,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bath2,141.13,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,LaundryRoom,252.56,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,UtilityRoom,308.27,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,WalkInCloset,308.27,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,FamilyRoom,420.95,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bedroom2,365.24,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Kitchen,365.24,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bedroom3,365.24,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bedroom,279.82,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Nook,279.82,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Hallway2,294.68,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Bedroom4,420.95,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,LivingRoom,477.84,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,Hallway,266.15,0,"Baseboard - 3 1/4"" stain grade"
+FNC,B3+,DiningRoom,420.95,0,"Baseboard - 3 1/4"" stain grade"
+FNC,CLROD,general,26.5,0,Closet rod
+FNC,CLROD,WalkInCloset,139.1,0,Closet rod
+FNC,CLROD,Bedroom2,33.12,0,Closet rod
+FNC,CLROD,Bedroom3,33.12,0,Closet rod
+FNC,CLROD,Bedroom,33.12,0,Closet rod
+FNC,CLROD,Bedroom4,33.12,0,Closet rod
+FNC,HR+,general,365.59,0,Handrail - detailed profile - softwood - wall mounted
+FNC,SH16,general,315.36,0,"Shelving - 16"" - in place"
+FNC,SH16,general,63.08,0,"Shelving - 16"" - in place"
+FNC,SH16,Bath,189.22,0,"Shelving - 16"" - in place"
+FNC,SH16,Bath3,189.22,0,"Shelving - 16"" - in place"
+FNC,SH16,Bath2,189.22,0,"Shelving - 16"" - in place"
+FNC,SH16,UtilityRoom,331.12,0,"Shelving - 16"" - in place"
+FNC,SH16,WalkInCloset,331.12,0,"Shelving - 16"" - in place"
+FNC,SH16,Bedroom2,78.84,0,"Shelving - 16"" - in place"
+FNC,SH16,Bedroom3,78.84,0,"Shelving - 16"" - in place"
+FNC,SH16,Bedroom,78.84,0,"Shelving - 16"" - in place"
+FNC,SH16,Bedroom4,78.84,0,"Shelving - 16"" - in place"
+FNC,SILL+,general,944.52,0,Window sill - stain grade
+FNH,DBX,general_main,383.84,0,Door lockset & deadbolt - exterior
+FNH,DORH,general,59,0,Door knob - interior
+FNH,DORH,general,59,0,Door knob - interior
+FNH,DORH,Bath,59,0,Door knob - interior
+FNH,DORH,Bath3,59,0,Door knob - interior
+FNH,DORH,Bath2,59,0,Door knob - interior
+FNH,DORH,LaundryRoom,59,0,Door knob - interior
+FNH,DORH,UtilityRoom,59,0,Door knob - interior
+FNH,DORH,WalkInCloset,59,0,Door knob - interior
+FNH,DORH,Bedroom2,59,0,Door knob - interior
+FNH,DORH,Bedroom3,59,0,Door knob - interior
+FNH,DORH,Bedroom,59,0,Door knob - interior
+FNH,DORH,Bedroom4,59,0,Door knob - interior
+FNH,SROD,Bath,42.38,0,Shower curtain rod
+FNH,TBAR,Bath,44.87,0,Towel bar
+FNH,TBAR,Bath3,44.87,0,Towel bar
+FNH,TBAR,Bath2,44.87,0,Towel bar
+FNH,TP,Bath,40.78,0,Toilet paper holder
+FNH,TP,Bath3,40.78,0,Toilet paper holder
+FNH,TP,Bath2,40.78,0,Toilet paper holder
+FNH,TR,Bath,44.92,0,Towel ring
+FNH,TR,Bath3,44.92,0,Towel ring
+FNH,TR,Bath2,44.92,0,Towel ring
+HVC,AC>,mech_single,5644.2,0,Central air conditioning system - 4 ton - up to 13 SEER
+HVC,BVENT,Bath,160.8,0,Bathroom ventilation fan
+HVC,BVENT,Bath3,160.8,0,Bathroom ventilation fan
+HVC,BVENT,Bath2,160.8,0,Bathroom ventilation fan
+HVC,DCT,mech_single,7027.78,0,Ductwork system - hot or cold air - 1200 to 1599 SF home
+HVC,DCTFN3,Bath,95.76,0,"Ductwork - flexible - non-insulated - 3"" round"
+HVC,DCTFN3,Bath3,95.76,0,"Ductwork - flexible - non-insulated - 3"" round"
+HVC,DCTFN3,Bath2,95.76,0,"Ductwork - flexible - non-insulated - 3"" round"
+HVC,DRNL,mech_single,98.14,0,Condensate drain line
+HVC,DVENT,general,101.74,0,Clothes dryer vent - installed
+HVC,FRFAH>,mech_single,5274.98,0,"Furnace - forced air - high efficiency - 100,000 BTU"
+HVC,TS+,general,205.57,0,Thermostat - High grade
+INS,BTF10,general,8850,0,"Batt insulation - 10"" - R30 - paper / foil faced"
+INS,BTF6,general,5486.35,0,"Batt insulation - 6"" - R19 - paper / foil faced"
+LIT,AV,EntryFoyer,85.6,0,Light fixture
+LIT,AV,FamilyRoom,85.6,0,Light fixture
+LIT,AV,Bedroom2,85.6,0,Light fixture
+LIT,AV,Bedroom3,85.6,0,Light fixture
+LIT,AV,Bedroom,85.6,0,Light fixture
+LIT,AV,Hallway2,85.6,0,Light fixture
+LIT,AV,Bedroom4,85.6,0,Light fixture
+LIT,AV,LivingRoom,85.6,0,Light fixture
+LIT,AV,Hallway,85.6,0,Light fixture
+LIT,AV,DiningRoom,85.6,0,Light fixture
+LIT,AV-,LaundryRoom,67.07,0,Light fixture - Standard grade
+LIT,AV-,UtilityRoom,67.07,0,Light fixture - Standard grade
+LIT,AV-,WalkInCloset,67.07,0,Light fixture - Standard grade
+LIT,AV-,Nook,67.07,0,Light fixture - Standard grade
+LIT,BAR3+,Bath3,199.36,0,Light bar - 3 lights - High grade
+LIT,BAR4,Bath2,120.15,0,Light bar - 4 lights
+LIT,BAR5,Bath,139.44,0,Light bar - 5 lights
+LIT,FL2-4+,general,151.87,0,Fluorescent - two tube - 4' - fixture w/lens
+LIT,FL2-4+,Kitchen,151.87,0,Fluorescent - two tube - 4' - fixture w/lens
+LIT,X,general_main,338.51,0,Exterior light fixture
+LIT,X-,general_main,91.59,0,Exterior light fixture - Standard grade
+LIT,X-,general_main,91.59,0,Exterior light fixture - Standard grade
+MSD,AV,Bath,137.02,0,"Mirror - 1/4"" plate glass"
+MSD,AV,Bath3,137.02,0,"Mirror - 1/4"" plate glass"
+MSD,AV,Bath2,137.02,0,"Mirror - 1/4"" plate glass"
+MSD,SDOR,Bath3,711.98,0,Shower door
+PLM,FAU,Kitchen,333.57,0,Sink faucet - Kitchen
+PLM,FAUBA,Bath,284.37,0,Sink faucet - Bathroom
+PLM,FAUBA,Bath3,284.37,0,Sink faucet - Bathroom
+PLM,FAUBA,Bath3,284.37,0,Sink faucet - Bathroom
+PLM,FAUBA,Bath2,284.37,0,Sink faucet - Bathroom
+PLM,RFIXP,Bath,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath3,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath2,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath2,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath2,593.41,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Bath2,197.8,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RFIXP,Kitchen,791.23,0,Rough-in plumbing - per fixture - w/PEX
+PLM,RGHFIX,LaundryRoom,892.28,0,Rough-in plumbing - per fixture
+PLM,SNK,Bath,374.41,0,Sink - single
+PLM,SNK,Bath3,374.41,0,Sink - single
+PLM,SNK,Bath2,374.41,0,Sink - single
+PLM,SNKD,Kitchen,507.47,0,Sink - double basin
+PLM,SWRPAN,Bath3,222.33,0,Shower pan
+PLM,TLT,Bath,663.12,0,Toilet
+PLM,TLT,Bath3,663.12,0,Toilet
+PLM,TLT,Bath2,663.12,0,Toilet
+PLM,TLTS,Bath,71.85,0,Toilet seat
+PLM,TLTS,Bath3,71.85,0,Toilet seat
+PLM,TLTS,Bath2,71.85,0,Toilet seat
+PLM,TSFAU,Bath,425.78,0,Tub/shower faucet
+PLM,TSFAU,Bath3,425.78,0,Tub/shower faucet
+PLM,TUB,Bath,1170.43,0,Bathtub
+PLM,TUB,Bath3,1170.43,0,Bathtub
+PLM,WBOX,LaundryRoom,295.58,0,Washing machine outlet box with valves
+PLM,WH50,mech_single,1836.39,0,Water heater - 50 gallon - Gas - 6 yr
+PNT,BF,Bedroom2,200.32,0,Paint bifold door set - slab only - 2 coats (per side)
+PNT,BF,Bedroom3,200.32,0,Paint bifold door set - slab only - 2 coats (per side)
+PNT,BF,Bedroom,200.32,0,Paint bifold door set - slab only - 2 coats (per side)
+PNT,BF,Bedroom4,200.32,0,Paint bifold door set - slab only - 2 coats (per side)
+PNT,BS,EntryFoyer,118.89,0,Stain & finish baseboard
+PNT,BS,Bath,77.86,0,Stain & finish baseboard
+PNT,BS,Bath3,107.3,0,Stain & finish baseboard
+PNT,BS,Bath2,59.97,0,Stain & finish baseboard
+PNT,BS,LaundryRoom,107.3,0,Stain & finish baseboard
+PNT,BS,UtilityRoom,130.99,0,Stain & finish baseboard
+PNT,BS,WalkInCloset,130.99,0,Stain & finish baseboard
+PNT,BS,FamilyRoom,178.84,0,Stain & finish baseboard
+PNT,BS,Bedroom2,155.18,0,Stain & finish baseboard
+PNT,BS,Kitchen,155.18,0,Stain & finish baseboard
+PNT,BS,Bedroom3,155.18,0,Stain & finish baseboard
+PNT,BS,Bedroom,118.89,0,Stain & finish baseboard
+PNT,BS,Nook,118.89,0,Stain & finish baseboard
+PNT,BS,Hallway2,125.19,0,Stain & finish baseboard
+PNT,BS,Bedroom4,178.84,0,Stain & finish baseboard
+PNT,BS,LivingRoom,203.03,0,Stain & finish baseboard
+PNT,BS,Hallway,113.07,0,Stain & finish baseboard
+PNT,BS,DiningRoom,178.84,0,Stain & finish baseboard
+PNT,DOR,general,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,general,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bath,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bath3,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bath2,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,LaundryRoom,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,UtilityRoom,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,WalkInCloset,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bedroom2,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bedroom3,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bedroom,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DOR,Bedroom4,151.76,0,Paint door slab only - 2 coats (per side)
+PNT,DORT,general,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,general,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bath,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bath3,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bath2,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,LaundryRoom,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,UtilityRoom,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,WalkInCloset,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bedroom2,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bedroom3,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bedroom,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,DORT,Bedroom4,127.8,0,Paint door/window trim & jamb - 2 coats (per side)
+PNT,HRS,general,63.91,0,Stain & finish handrail - wall mounted
+PNT,P,EntryFoyer,120.24,0,Paint the surface area - one coat
+PNT,P,Bath,48.84,0,Paint the surface area - one coat
+PNT,P,Bath3,96.83,0,Paint the surface area - one coat
+PNT,P,Bath2,30.59,0,Paint the surface area - one coat
+PNT,P,LaundryRoom,96.83,0,Paint the surface area - one coat
+PNT,P,UtilityRoom,144.71,0,Paint the surface area - one coat
+PNT,P,WalkInCloset,144.71,0,Paint the surface area - one coat
+PNT,P,FamilyRoom,369.43,0,Paint the surface area - one coat
+PNT,P,Bedroom2,203.63,0,Paint the surface area - one coat
+PNT,P,Kitchen,200.11,0,Paint the surface area - one coat
+PNT,P,Bedroom3,203.63,0,Paint the surface area - one coat
+PNT,P,Bedroom,120.24,0,Paint the surface area - one coat
+PNT,P,Nook,120.24,0,Paint the surface area - one coat
+PNT,P,Hallway2,108.86,0,Paint the surface area - one coat
+PNT,P,Bedroom4,270.99,0,Paint the surface area - one coat
+PNT,P,LivingRoom,480.58,0,Paint the surface area - one coat
+PNT,P,Hallway,72.04,0,Paint the surface area - one coat
+PNT,P,DiningRoom,261.26,0,Paint the surface area - one coat
+PNT,S,EntryFoyer,113.86,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bath,46.24,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bath3,91.69,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bath2,28.96,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,LaundryRoom,91.69,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,UtilityRoom,137.03,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,WalkInCloset,137.03,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,FamilyRoom,349.8,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bedroom2,192.82,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Kitchen,189.48,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bedroom3,192.82,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bedroom,113.86,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Nook,113.86,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Hallway2,103.08,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Bedroom4,256.6,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,LivingRoom,455.06,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,Hallway,68.23,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,S,DiningRoom,247.4,0,Seal the surface area w/latex based stain blocker - one coat
+PNT,SHW,general,186,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,general,37.2,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bath,111.6,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bath3,111.6,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bath2,111.6,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,UtilityRoom,195.31,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,WalkInCloset,195.31,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bedroom2,46.51,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bedroom3,46.51,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bedroom,46.51,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SHW,Bedroom4,46.51,0,"Seal & paint wood shelving, 12""- 24"" width"
+PNT,SILL,general,899.64,0,Seal & paint window sill
+PNT,SP,general,202.18,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,general,202.18,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,EntryFoyer,585.79,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bath,383.61,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bath3,528.76,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bath2,295.48,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,LaundryRoom,528.76,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,UtilityRoom,645.4,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,WalkInCloset,645.4,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,FamilyRoom,881.27,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bedroom2,764.63,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Kitchen,764.63,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bedroom3,764.63,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bedroom,585.79,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Nook,585.79,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Hallway2,616.89,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Bedroom4,881.27,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,LivingRoom,1000.53,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,Hallway,557.29,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+PNT,SP,DiningRoom,881.27,0,Seal/prime (1 coat) then paint (1 coat) the surface area
+TIL,BMUD,Bath,817.08,0,Mortar bed for tile
+TIL,BMUD,Bath3,1173.62,0,Mortar bed for tile
+TIL,SWR,Bath3,3402.52,0,Tile shower - 61 to 100 SF
+TIL,TUB,Bath,1786.28,0,Tile tub surround - up to 60 SF
+WDP,6V,general_main,1391.12,0,6-0 6-8 vinyl sliding patio door
diff --git a/tests/data/bldgs/case3/readme.txt b/tests/data/bldgs/case3/readme.txt
new file mode 100644
index 0000000..3dd0500
--- /dev/null
+++ b/tests/data/bldgs/case3/readme.txt
@@ -0,0 +1,3 @@
+2024-05-09: case provided by Heather
+
+https://mail.google.com/mail/u/2/#inbox/FMfcgzGxTFXMrNkBZRjsBTvKsXcSlZss
\ No newline at end of file