move 'hidden' property from ui spec into input classes

This commit is contained in:
Emily Soth 2025-05-07 16:33:04 -07:00
parent b6f3168024
commit 382f5f4f2c
29 changed files with 53 additions and 91 deletions

View File

@ -116,8 +116,7 @@ MODEL_SPEC = spec.build_model_spec({
['lulc_path', 'biophysical_table_path', 'seasonality_constant'],
['watersheds_path', 'sub_watersheds_path'],
['demand_table_path', 'valuation_table_path']
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": ["lulc_path",

View File

@ -50,7 +50,6 @@ MODEL_SPEC = spec.build_model_spec({
['calc_sequestration', 'lulc_alt_path'],
['do_valuation', 'lulc_bas_year', 'lulc_alt_year', 'price_per_metric_ton_of_c', 'discount_rate', 'rate_change'],
],
"hidden": ["n_workers"],
"forum_tag": 'carbon'
},
"args_with_spatial_overlap": {

View File

@ -261,8 +261,7 @@ MODEL_SPEC = spec.build_model_spec({
['workspace_dir', 'results_suffix'],
['landcover_snapshot_csv', 'biophysical_table_path', 'landcover_transitions_table', 'analysis_year'],
['do_economic_analysis', 'use_price_table', 'price', 'inflation_rate', 'price_table_path', 'discount_rate'],
],
"hidden": ["n_workers"]
]
},
"args": {
"workspace_dir": spec.WORKSPACE,

View File

@ -26,8 +26,7 @@ MODEL_SPEC = spec.build_model_spec({
"order": [
['workspace_dir', 'results_suffix'],
['lulc_lookup_table_path', 'landcover_snapshot_csv']
],
"hidden": ["n_workers"]
]
},
"args": {
"workspace_dir": spec.WORKSPACE,

View File

@ -158,7 +158,6 @@ MODEL_SPEC = spec.build_model_spec({
['population_raster_path', 'population_radius'],
['slr_vector_path', 'slr_field']
],
"hidden": ["n_workers"],
"dropdown_functions": {
"slr_field": lambda args: get_vector_colnames(args['slr_vector_path'])
}

View File

@ -249,8 +249,7 @@ MODEL_SPEC = spec.build_model_spec({
"order": [
['workspace_dir', 'results_suffix'],
['model_data_path', 'landcover_raster_path', 'landcover_to_crop_table_path', 'aggregate_polygon_path']
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": [

View File

@ -75,8 +75,7 @@ MODEL_SPEC = spec.build_model_spec({
"order": [
['workspace_dir', 'results_suffix'],
['model_data_path', 'landcover_raster_path', 'landcover_to_crop_table_path', 'fertilization_rate_table_path', 'aggregate_polygon_path'],
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": ["landcover_raster_path", "aggregate_polygon_path"],

View File

@ -33,8 +33,7 @@ MODEL_SPEC = spec.build_model_spec({
['workspace_dir', 'results_suffix'],
['dem_path', 'detect_pour_points', 'outlet_vector_path', 'skip_invalid_geometry'],
['snap_points', 'flow_threshold', 'snap_distance'],
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": ["dem_path", "outlet_vector_path"],

View File

@ -42,8 +42,7 @@ MODEL_SPEC = spec.build_model_spec({
['lulc_raster_path', 'biophysical_table_path', 'pools_to_calculate'],
['compute_forest_edge_effects', 'tropical_forest_edge_carbon_model_vector_path', 'n_nearest_model_points', 'biomass_to_carbon_conversion_factor'],
['aoi_vector_path']
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": ["aoi_vector_path", "lulc_raster_path"],

View File

@ -42,8 +42,7 @@ MODEL_SPEC = spec.build_model_spec({
['workspace_dir', 'results_suffix'],
['lulc_cur_path', 'lulc_fut_path', 'lulc_bas_path'],
['threats_table_path', 'access_vector_path', 'sensitivity_table_path', 'half_saturation_constant'],
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": [

View File

@ -62,8 +62,7 @@ MODEL_SPEC = spec.build_model_spec({
['aoi_vector_path'],
['n_overlapping_stressors'],
['visualize_outputs']
],
"hidden": ["n_workers"]
]
},
"args": {
"workspace_dir": spec.WORKSPACE,

View File

@ -35,8 +35,7 @@ MODEL_SPEC = spec.build_model_spec({
['calc_p'],
['calc_n', 'subsurface_critical_length_n', 'subsurface_eff_n'],
['flow_dir_algorithm', 'threshold_flow_accumulation', 'k_param', 'runoff_proxy_av'],
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": ["dem_path", "lulc_path", "runoff_proxy_path",

View File

@ -32,8 +32,7 @@ MODEL_SPEC = spec.build_model_spec({
['workspace_dir', 'results_suffix'],
['landcover_raster_path', 'landcover_biophysical_table_path'],
['guild_table_path', 'farm_vector_path']
],
"hidden": ["n_workers"]
]
},
"args": {
"workspace_dir": spec.WORKSPACE,

View File

@ -111,8 +111,7 @@ MODEL_SPEC = spec.build_model_spec({
['start_year', 'end_year'],
['compute_regression', 'predictor_table_path', 'scenario_predictor_table_path'],
['grid_aoi', 'grid_type', 'cell_size'],
],
"hidden": ['n_workers', 'hostname', 'port']
]
},
"args": {
"workspace_dir": spec.WORKSPACE,
@ -128,7 +127,8 @@ MODEL_SPEC = spec.build_model_spec({
"about": gettext(
"FQDN to a recreation server. If not provided, a default is "
"assumed."),
"name": gettext("hostname")
"name": gettext("hostname"),
"hidden": True
},
"port": {
"type": "number",
@ -138,7 +138,8 @@ MODEL_SPEC = spec.build_model_spec({
"about": gettext(
"the port on ``hostname`` to use for contacting the "
"recreation server."),
"name": gettext("port")
"name": gettext("port"),
"hidden": True
},
"start_year": {
"type": "number",

View File

@ -30,8 +30,7 @@ MODEL_SPEC = spec.build_model_spec({
['calculate_flow_direction'],
['calculate_flow_accumulation'],
['calculate_stream_threshold', 'threshold_flow_accumulation', 'calculate_downslope_distance', 'calculate_stream_order', 'calculate_subwatersheds']
],
"hidden": ["n_workers"]
]
},
"args": {
"workspace_dir": spec.WORKSPACE,

View File

@ -39,8 +39,7 @@ MODEL_SPEC = spec.build_model_spec({
['base_lulc_path', 'aoi_path'],
['area_to_convert', 'focal_landcover_codes', 'convertible_landcover_codes', 'replacement_lucode'],
['convert_farthest_from_edge', 'convert_nearest_to_edge', 'n_fragmentation_steps']
],
"hidden": ["n_workers"]
]
},
"args": {
"workspace_dir": spec.WORKSPACE,

View File

@ -56,8 +56,7 @@ MODEL_SPEC = spec.build_model_spec({
['workspace_dir', 'results_suffix'],
['aoi_path', 'structure_path', 'dem_path', 'refraction'],
['do_valuation', 'valuation_function', 'a_coef', 'b_coef', 'max_valuation_radius'],
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": ["aoi_path", "structure_path", "dem_path"],

View File

@ -39,8 +39,7 @@ MODEL_SPEC = spec.build_model_spec({
['lulc_path', 'biophysical_table_path'],
['watersheds_path', 'drainage_path'],
['flow_dir_algorithm', 'threshold_flow_accumulation', 'k_param', 'sdr_max', 'ic_0_param', 'l_max']
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": ["dem_path", "erosivity_path", "erodibility_path",

View File

@ -42,8 +42,7 @@ MODEL_SPEC = spec.build_model_spec({
['user_defined_local_recharge', 'l_path', 'et0_dir', 'precip_dir', 'soil_group_path'],
['monthly_alpha', 'alpha_m', 'monthly_alpha_path'],
['user_defined_climate_zones', 'rain_events_table_path', 'climate_zone_table_path', 'climate_zone_raster_path'],
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": ["dem_raster_path", "lulc_raster_path",

View File

@ -180,12 +180,17 @@ class Input:
under a certain condition (such as when running the model in a mode
where the input is not used), provide a string expression that
evaluates to a boolean to describe this condition.
hidden: Whether to hide the input from the model input form in the
workbench. Use this if the value should not be configurable from
the input form, such as if it's pulled in from another source.
Defaults to False.
"""
id: str = ''
name: str = ''
about: str = ''
required: typing.Union[bool, str] = True
allowed: typing.Union[bool, str] = True
hidden: bool = False
@dataclasses.dataclass
class Output:
@ -1049,9 +1054,11 @@ class CSVOutput(Output):
The `key` of each input must match the corresponding column header.
rows: An iterable of `Output`s representing the table's rows. The
`key` of each input must match the corresponding row header.
index_col: The header name of the column that is the index of the table.
"""
columns: typing.Union[typing.Iterable[Output], None] = None
rows: typing.Union[typing.Iterable[Output], None] = None
index_col: typing.Union[str, None] = None
@dataclasses.dataclass
class DirectoryOutput(Output):
@ -1132,7 +1139,6 @@ class OptionStringOutput(Output):
@dataclasses.dataclass
class UISpec:
order: typing.Union[list, None] = None
hidden: list = None
dropdown_functions: dict = dataclasses.field(default_factory=dict)
@dataclasses.dataclass
@ -1205,7 +1211,6 @@ def build_model_spec(model_spec):
build_output_spec(argkey, argspec) for argkey, argspec in model_spec['outputs'].items()]
ui_spec = UISpec(
order=model_spec['ui_spec']['order'],
hidden=model_spec['ui_spec'].get('hidden', None),
dropdown_functions=model_spec['ui_spec'].get('dropdown_functions', None))
return ModelSpec(
model_id=model_spec['model_id'],
@ -1224,7 +1229,8 @@ def build_input_spec(argkey, arg):
'name': arg.get('name', None),
'about': arg.get('about', None),
'required': arg.get('required', True),
'allowed': arg.get('allowed', True)
'allowed': arg.get('allowed', True),
'hidden': arg.get('hidden', False)
}
t = arg['type']
@ -1342,8 +1348,7 @@ def build_output_spec(key, spec):
if t == 'number':
return NumberOutput(
**base_attrs,
units=spec['units'],
expression=None)
units=spec['units'])
elif t == 'integer':
return IntegerOutput(**base_attrs)
@ -1357,44 +1362,33 @@ def build_output_spec(key, spec):
elif t == 'raster':
return SingleBandRasterOutput(
**base_attrs,
band=build_output_spec(1, spec['bands'][1]),
projected=None,
projection_units=None)
band=build_output_spec(1, spec['bands'][1]))
elif t == 'vector':
return VectorOutput(
**base_attrs,
geometries=spec['geometries'],
fields=[build_output_spec(key, field_spec)
for key, field_spec in spec['fields'].items()],
projected=None,
projection_units=None)
for key, field_spec in spec['fields'].items()])
elif t == 'csv':
return CSVOutput(
**base_attrs,
columns=[
build_output_spec(key, col_spec) for key, col_spec in spec['columns'].items()],
rows=None,
index_col=spec.get('index_col', None))
elif t == 'directory':
return DirectoryOutput(
contents=[
build_output_spec(k, v) for k, v in spec['contents'].items()],
permissions=None,
must_exist=None,
**base_attrs)
elif t == 'freestyle_string':
return StringOutput(
**base_attrs,
regexp=spec.get('regexp', None))
return StringOutput(**base_attrs)
elif t == 'option_string':
return OptionStringOutput(
**base_attrs,
options=spec['options'])
return OptionStringOutput(options=spec['options'])
elif t == 'file':
return FileOutput(**base_attrs)
@ -1437,7 +1431,8 @@ N_WORKERS = {
"type": "number",
"units": u.none,
"required": False,
"expression": "value >= -1"
"expression": "value >= -1",
"hidden": True
}
METER_RASTER = {

View File

@ -36,8 +36,7 @@ MODEL_SPEC = spec.build_model_spec({
['lulc_path', 'soil_group_path', 'precipitation_path', 'biophysical_table'],
['adjust_retention_ratios', 'retention_radius', 'road_centerlines_path'],
['aggregate_areas_path', 'replacement_cost'],
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": ["lulc_path", "soil_group_path", "precipitation_path",

View File

@ -42,8 +42,7 @@ MODEL_SPEC = spec.build_model_spec({
['do_energy_valuation', 'building_vector_path', 'energy_consumption_table_path'],
['do_productivity_valuation', 'avg_rel_humidity'],
['cc_weight_shade', 'cc_weight_albedo', 'cc_weight_eti'],
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": ["lulc_raster_path", "ref_eto_raster_path",

View File

@ -32,8 +32,7 @@ MODEL_SPEC = spec.build_model_spec({
['aoi_watersheds_path', 'rainfall_depth'],
['lulc_path', 'curve_number_table_path', 'soils_hydrological_group_raster_path'],
['built_infrastructure_vector_path', 'infrastructure_damage_loss_table_path']
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": ["aoi_watersheds_path", "lulc_path",

View File

@ -49,8 +49,7 @@ MODEL_SPEC = spec.build_model_spec({
['lulc_raster_path', 'lulc_attribute_table'],
['population_raster_path', 'admin_boundaries_vector_path', 'population_group_radii_table', 'urban_nature_demand', 'aggregate_by_pop_group'],
['search_radius_mode', 'decay_function', 'search_radius']
],
"hidden": ["n_workers"]
]
},
'args_with_spatial_overlap': {
'spatial_keys': [

View File

@ -141,8 +141,7 @@ MODEL_SPEC = spec.build_model_spec({
['wave_base_data_path', 'analysis_area', 'aoi_path', 'dem_path'],
['machine_perf_path', 'machine_param_path'],
['valuation_container', 'land_gridPts_path', 'machine_econ_path', 'number_of_machines'],
],
"hidden": ["n_workers"]
]
},
"args": {
"workspace_dir": spec.WORKSPACE,

View File

@ -101,8 +101,7 @@ MODEL_SPEC = spec.build_model_spec({
['wind_data_path', 'aoi_vector_path', 'bathymetry_path', 'land_polygon_vector_path', 'global_wind_parameters_path'],
['turbine_parameters_path', 'number_of_turbines', 'min_depth', 'max_depth', 'min_distance', 'max_distance'],
['valuation_container', 'foundation_cost', 'discount_rate', 'grid_points_path', 'avg_grid_distance', 'price_table', 'wind_schedule', 'wind_price', 'rate_change'],
],
"hidden": ["n_workers"]
]
},
"args_with_spatial_overlap": {
"spatial_keys": ["aoi_vector_path", "bathymetry_path",

View File

@ -142,7 +142,6 @@ class ValidateModelSpecs(unittest.TestCase):
self.assertIsInstance(
model.MODEL_SPEC.ui_spec.dropdown_functions, dict)
self.assertIsInstance(model.MODEL_SPEC.ui_spec.order, list)
self.assertIsInstance(model.MODEL_SPEC.ui_spec.hidden, list)
found_keys = set()
for group in model.MODEL_SPEC.ui_spec.order:
self.assertIsInstance(group, list)
@ -150,10 +149,9 @@ class ValidateModelSpecs(unittest.TestCase):
self.assertIsInstance(key, str)
self.assertNotIn(key, found_keys)
found_keys.add(key)
for key in model.MODEL_SPEC.ui_spec.hidden:
self.assertIsInstance(key, str)
self.assertNotIn(key, found_keys)
found_keys.add(key)
for arg_spec in model.MODEL_SPEC.inputs:
if arg_spec.hidden is True:
found_keys.add(arg_spec.id)
self.assertEqual(found_keys, set([s.id for s in model.MODEL_SPEC.inputs]))
# validate that each arg meets the expected pattern
@ -267,7 +265,9 @@ class ValidateModelSpecs(unittest.TestCase):
if output_spec.created_if:
# should be an arg key indicating that the output is
# created if that arg is provided or checked
self.assertIsInstance(output_spec.created_if, str)
self.assertTrue(
isinstance(output_spec.created_if, str) or
isinstance(output_spec.created_if, bool))
def validate_args(self, arg, name, parent_type=None):
"""

View File

@ -67,7 +67,7 @@ class UsageLoggingTests(unittest.TestCase):
model_spec = spec.ModelSpec(
model_id='', model_title='', userguide=None,
aliases=None, ui_spec=spec.UISpec(order=[], hidden={}),
aliases=None, ui_spec=spec.UISpec(order=[]),
inputs=[
spec.SingleBandRasterInput(id='raster', band=spec.Input()),
spec.VectorInput(id='vector', geometries={}, fields={}),

View File

@ -36,13 +36,12 @@ from natcap.invest.spec import (
NumberInput,
IntegerInput,
RatioInput,
PercentInput,
OtherInput)
PercentInput)
gdal.UseExceptions()
def ui_spec_with_defaults(order=[], hidden=[]):
return UISpec(order=order, hidden=hidden)
def ui_spec_with_defaults(order=[]):
return UISpec(order=order)
def model_spec_with_defaults(model_id='', model_title='', userguide='', aliases=None,
ui_spec=ui_spec_with_defaults(), inputs={}, outputs={},
@ -1984,15 +1983,6 @@ class TestValidationFromSpec(unittest.TestCase):
# TODO: directory contents are not actually validated right now
self.assertEqual([], validation.validate(args, spec))
def test_validation_other(self):
"""Validation: verify no error when 'other' type."""
from natcap.invest import validation
spec = model_spec_with_defaults(inputs=[
OtherInput(id="number_a")
])
args = {'number_a': 1}
self.assertEqual([], validation.validate(args, spec))
def test_conditional_validity_recursive(self):
"""Validation: check that we can require from nested conditions."""
from natcap.invest import validation