From cee2897b1e5ace4a697d40450d87d53d0ef3a853 Mon Sep 17 00:00:00 2001 From: zlg Date: Tue, 30 Apr 2019 20:34:15 -0700 Subject: Release version 0.3 beta 4 This release brings JSON import and export support. --- setup.py | 3 +- src/vgstash_cli.py | 88 +++++++++++++++++++++++++++------------------- tests/data/test_import.yml | 8 ++--- tests/test_vgstash_cli.py | 43 +++++++++++++++++++++- 4 files changed, 100 insertions(+), 42 deletions(-) diff --git a/setup.py b/setup.py index f978307..4cfc126 100755 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ with open('README.md') as f: setup( name='vgstash', - version='0.3-beta3', + version='0.3-beta4', description='a video game collection management module, backed by SQLite', long_description=readme, long_description_content_type="text/markdown; variant=CommonMark", @@ -30,6 +30,7 @@ setup( install_requires=[ 'Click>=6.0', # for CLI 'PyYAML', # import/export YAML files + 'json' # part of the standard library, but someone might've compiled Python without it ], classifiers=( "Development Status :: 4 - Beta", diff --git a/src/vgstash_cli.py b/src/vgstash_cli.py index 7996086..b92201f 100644 --- a/src/vgstash_cli.py +++ b/src/vgstash_cli.py @@ -6,6 +6,7 @@ import subprocess import sys import tempfile import yaml +import json # Click also has this, but it doesn't support a fallback value. from shutil import get_terminal_size @@ -207,7 +208,7 @@ def notes(title, system, edit): @cli.command("import") -@click.option("--format", "-f", type=click.Choice(["yaml"]), required=False, default="yaml") +@click.option("--format", "-f", type=click.Choice(["yaml", "json"]), required=False, default="yaml") @click.option("--update", "-u", is_flag=True, default=False, help="Overwrite existing games with the file's data") @click.argument("filepath", type=click.Path( @@ -223,34 +224,42 @@ def import_file(format, filepath, update): Import game data from an external file matching the chosen format. The default format is YAML. + + Available formats: + + * JSON + * YAML """ - if format == "yaml": - with open(filepath) as fp: + with open(filepath) as fp: + if format == "yaml": data = yaml.safe_load(fp) - db = get_db() - count = len(data) - for game in data: - try: - db.add_game( - vgstash.Game( - game["title"], - game["system"], - game["ownership"], - game["progress"], - game["notes"] - ), - update=update - ) - except sqlite3.IntegrityError as e: - count -= 1 - if count > 0: - click.echo("Successfully imported {} games from {}.".format(count, filepath)) - else: - click.echo("Couldn't import any games. Is the YAML file formatted correctly?") + if format == "json": + data = json.load(fp) + db = get_db() + count = len(data) + for game in data: + try: + db.add_game( + vgstash.Game( + game["title"], + game["system"], + game["ownership"], + game["progress"], + game["notes"] + ), + update=update + ) + except sqlite3.IntegrityError as e: + # skip games that already exist + count -= 1 + if count > 0: + click.echo("Successfully imported {} games from {}.".format(count, filepath)) + else: + click.echo("Couldn't import any games. Is the file formatted correctly?") @cli.command("export") -@click.option("--format", "-f", type=click.Choice(["yaml"]), required=False, default="yaml") +@click.option("--format", "-f", type=click.Choice(["yaml", "json"]), required=False, default="yaml") @click.argument("filepath", type=click.Path( exists=False, @@ -267,22 +276,29 @@ def export_file(format, filepath): Export the game database to a file written in the chosen format. The default format is YAML. + + Available formats: + + * JSON + * YAML """ db = get_db() data = db.list_games() game_set = [] - # Time to re-read master's code - if format == "yaml": - for game in data: - g = {} - for field in game.keys(): - g.update({field: game[field]}) - game_set.append(g) - with open(filepath, "w") as fp: + # Time to re-read the master branch's code + for game in data: + g = {} + for field in game.keys(): + g.update({field: game[field]}) + game_set.append(g) + with open(filepath, "w") as fp: + if format == "yaml": yaml.dump(game_set, fp, default_flow_style=False, indent=4, allow_unicode=True) - if len(game_set) > 0: - click.echo("Successfully exported {} games to {}.".format(len(game_set), filepath)) - else: - click.echo("Could not export any games; have you made sure your collection has games in it?") + if format == "json": + json.dump(game_set, fp, allow_nan=False, indent=1, skipkeys=True, sort_keys=True) + if len(game_set) > 0: + click.echo("Successfully exported {} games to {}.".format(len(game_set), filepath)) + else: + click.echo("Could not export any games; have you made sure your collection has games in it?") diff --git a/tests/data/test_import.yml b/tests/data/test_import.yml index eb93579..b01538f 100644 --- a/tests/data/test_import.yml +++ b/tests/data/test_import.yml @@ -1,13 +1,13 @@ # vgstash DB file version 0.2 - ownership: 1 progress: 3 - system: NES - title: Super Mario Bros. + system: GBA + title: Fire Emblem notes: '' - ownership: 2 progress: 4 - system: 3DS - title: The Legend of Zelda + system: Switch + title: Puyo Puyo Tetris notes: '' - ownership: 2 progress: 3 diff --git a/tests/test_vgstash_cli.py b/tests/test_vgstash_cli.py index c3b60d6..30eff62 100644 --- a/tests/test_vgstash_cli.py +++ b/tests/test_vgstash_cli.py @@ -241,6 +241,38 @@ def test_notes_edit(): assert list_result.exit_code == 0 +def test_import_file_json(): + runner = CliRunner() + result = runner.invoke(vgstash_cli.cli, ["import", "tests/data/test_import.json"]) + if verbose: + print(result.output) + assert result.exit_code == 0 + assert result.output == "Successfully imported 2 games from {}.\n".format(os.path.join(os.getcwd(), "tests/data/test_import.json")) + + # List the results to make sure they match what the editor has. + list_runner = CliRunner() + list_result = runner.invoke(vgstash_cli.cli, ['list', '-w', '40']) + if verbose: + print(list_result.output) + assert list_result.exit_code == 0 + + +def test_import_file_json_update(): + runner = CliRunner() + result = runner.invoke(vgstash_cli.cli, ["import", "tests/data/test_import.json", "-u"]) + if verbose: + print(result.output) + assert result.exit_code == 0 + assert result.output == "Successfully imported 3 games from {}.\n".format(os.path.join(os.getcwd(), "tests/data/test_import.json")) + + # List the results to make sure they match what the editor has. + list_runner = CliRunner() + list_result = runner.invoke(vgstash_cli.cli, ['list', '-w', '40']) + if verbose: + print(list_result.output) + assert list_result.exit_code == 0 + + def test_import_file_yaml(): runner = CliRunner() result = runner.invoke(vgstash_cli.cli, ["import", "tests/data/test_import.yml"]) @@ -273,10 +305,19 @@ def test_import_file_yaml_update(): assert list_result.exit_code == 0 +def test_export_file_json(): + runner = CliRunner() + result = runner.invoke(vgstash_cli.cli, ["export", "-f", "json", "tests/data/test_export.json"]) + if verbose: + print(result.output) + assert result.exit_code == 0 + assert result.output == "Successfully exported 8 games to {}.\n".format(os.path.join(os.getcwd(), "tests/data/test_export.json")) + + def test_export_file_yaml(): runner = CliRunner() result = runner.invoke(vgstash_cli.cli, ["export", "-f", "yaml", "tests/data/test_export.yml"]) if verbose: print(result.output) assert result.exit_code == 0 - assert result.output == "Successfully exported 6 games to {}.\n".format(os.path.join(os.getcwd(), "tests/data/test_export.yml")) + assert result.output == "Successfully exported 8 games to {}.\n".format(os.path.join(os.getcwd(), "tests/data/test_export.yml")) -- cgit v1.2.3-54-g00ecf