From 522fbf059635508af979e1b36440fb817148d90e Mon Sep 17 00:00:00 2001 From: Mostapha Date: Wed, 17 Feb 2021 17:04:35 -0500 Subject: [PATCH] feat(cli): add command to filter a JSON array by key:value This is required to handle models with sensor groups --- honeybee_radiance_folder/cli.py | 49 +++++++++++++++++-- tests/assets/project_folder/grid_info.json | 56 ++++++++++++++++++++++ tests/cli.py | 38 +++++++++++++++ 3 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 tests/assets/project_folder/grid_info.json create mode 100644 tests/cli.py diff --git a/honeybee_radiance_folder/cli.py b/honeybee_radiance_folder/cli.py index 81510be..9512b43 100644 --- a/honeybee_radiance_folder/cli.py +++ b/honeybee_radiance_folder/cli.py @@ -238,9 +238,9 @@ def dynamic_scene(radiance_folder, model, outdoor, log_file): sys.exit(0) -@folder.command('filter') +@folder.command('filter-folder') @click.argument( - 'folder', #type=click.Path(exists=True, file_okay=False, resolve_path=True) + 'folder', type=click.Path(exists=True, file_okay=False, resolve_path=True) ) @click.argument('pattern') @click.option( @@ -271,4 +271,47 @@ def filter_folder(folder, pattern, log_file): else: sys.exit(0) -# {stem, suffix, name, parent} + +@folder.command('filter-file') +@click.argument( + 'input-file', + type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True) +) +@click.argument('pattern', default='*:*') +@click.option('--output-file', '-of', type=click.File(mode='w'), default='-') +@click.option( + '--keep/--remove', is_flag=True, help='A flag to switch between keeping the objects ' + 'or removing them from the input list.', default=True, show_default=True +) +def filter_json_file(input_file, pattern, output_file, keep): + """Filter a list fo JSON objects based on value for a specific key. + + \b + Args: + input-file: Path to input JSON file. Input JSON file should be an array of + JSON objects. + pattern: Two string values separated by a ``:``. For example group:daylight + will keep/remove the objects when the value for group key is set to daylight. + + """ + try: + key, value = [v.strip() for v in pattern.split(':')] + + with open(input_file) as inf: + data = json.load(inf) + + if key == value == '*': + # no filtering. pass the values as is. + output_file.write(json.dumps(data)) + sys.exit(0) + if keep: + filtered_data = [obj for obj in data if obj[key] == value] + else: + filtered_data = [obj for obj in data if obj[key] != value] + + output_file.write(json.dumps(filtered_data)) + except Exception as e: + _logger.exception('Failed to filter objects in input file.\n{}'.format(e)) + sys.exit(1) + else: + sys.exit(0) diff --git a/tests/assets/project_folder/grid_info.json b/tests/assets/project_folder/grid_info.json new file mode 100644 index 0000000..186fe9e --- /dev/null +++ b/tests/assets/project_folder/grid_info.json @@ -0,0 +1,56 @@ +[ + { + "name": "west_window", + "identifier": "west_window", + "count": 96, + "group": "apertures" + }, + { + "name": "east_window", + "identifier": "east_window", + "count": 96, + "group": "apertures" + }, + { + "name": "north_window", + "identifier": "north_window", + "count": 64, + "group": "apertures" + }, + { + "name": "south_window", + "identifier": "south_window", + "count": 64, + "group": "apertures" + }, + { + "name": "Occ_Regions_East_T3_area", + "identifier": "Occ_Regions_East_T3_area", + "count": 1436, + "group": "occ_regions" + }, + { + "name": "Occ_Regions_North_area", + "identifier": "Occ_Regions_North_area", + "count": 1106, + "group": "occ_regions" + }, + { + "name": "Occ_Regions_West_T3_area", + "identifier": "Occ_Regions_West_T3_area", + "count": 1146, + "group": "occ_regions" + }, + { + "name": "Occ_Regions_South_T4_area", + "identifier": "Occ_Regions_South_T4_area", + "count": 906, + "group": "occ_regions" + }, + { + "name": "ground_floor", + "identifier": "ground_floor", + "count": 3196, + "group": "daylight_grids" + } +] \ No newline at end of file diff --git a/tests/cli.py b/tests/cli.py new file mode 100644 index 0000000..739b666 --- /dev/null +++ b/tests/cli.py @@ -0,0 +1,38 @@ +from click.testing import CliRunner +from honeybee_radiance_folder.cli import filter_json_file +import json +import os + + +def test_filter_file(): + runner = CliRunner() + input_file = './tests/assets/project_folder/grid_info.json' + output_file = './tests/assets/temp/grid_filtered_0.json' + result = runner.invoke( + filter_json_file, [ + input_file, 'group:daylight_grids', '--output-file', output_file + ] + ) + assert result.exit_code == 0 + # check the file is created + with open(output_file) as inf: + data = json.load(inf) + assert len(data) == 1 + os.unlink(output_file) + + +def test_filter_file_remove(): + runner = CliRunner() + input_file = './tests/assets/project_folder/grid_info.json' + output_file = './tests/assets/project_folder/grid_filtered_1.json' + result = runner.invoke( + filter_json_file, [ + input_file, 'group:daylight_grids', '--output-file', output_file, '--remove' + ] + ) + assert result.exit_code == 0 + # check the file is created + with open(output_file) as inf: + data = json.load(inf) + assert len(data) == 8 + os.unlink(output_file)