From 00b1799b32dc1a4637b780b22b2c539a5ffd0c05 Mon Sep 17 00:00:00 2001 From: Tsuyoshi Hombashi Date: Sat, 22 Oct 2016 22:26:49 +0900 Subject: [PATCH 1/8] Update MANIFEST.in --- MANIFEST.in | 1 - 1 file changed, 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 04a35bd..c13f04e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,7 +4,6 @@ include README.rst include setup.cfg include tox.ini -recursive-include docs * recursive-include requirements * recursive-include test * From a3f1633cf9c6ccf72555f249633df80cbf7dbb56 Mon Sep 17 00:00:00 2001 From: Tsuyoshi Hombashi Date: Sat, 22 Oct 2016 22:27:06 +0900 Subject: [PATCH 2/8] Add logging --- sqlitebiter/sqlitebiter.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sqlitebiter/sqlitebiter.py b/sqlitebiter/sqlitebiter.py index a351131..f10b6a5 100644 --- a/sqlitebiter/sqlitebiter.py +++ b/sqlitebiter/sqlitebiter.py @@ -113,6 +113,9 @@ def file(ctx, files, output_path): Convert CSV/Excel/HTML/JSON file(s) to a SQLite database file. """ + if dataproperty.is_empty_sequence(files): + return 0 + con = create_database(output_path) result_counter = ResultCounter() @@ -127,6 +130,8 @@ def file(ctx, files, output_path): try: loader = LoaderFactory.get_loader(file_path) except LoaderNotFound: + logger.debug( + "loader not found that coincide with '{}'".format(file_path)) continue try: From bb41504cfa843ff6b338d46364f322f1f250e03d Mon Sep 17 00:00:00 2001 From: Tsuyoshi Hombashi Date: Sat, 22 Oct 2016 22:29:50 +0900 Subject: [PATCH 3/8] Add a test case --- test/test_sqlitebiter.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/test_sqlitebiter.py b/test/test_sqlitebiter.py index 9d9ec1f..de97bad 100644 --- a/test/test_sqlitebiter.py +++ b/test/test_sqlitebiter.py @@ -271,6 +271,17 @@ def test_normal_smoke(self): cmd, ["file", file_path, "-o", db_path]) assert result.exit_code == 0, file_path + def test_abnormal_empty(self): + runner = CliRunner() + + with runner.isolated_filesystem(): + result = runner.invoke( + cmd, ["file"]) + + assert result.exit_code == 0 + assert not path.Path( + "out.sqlite").exists(), "output file must not exist" + def test_abnormal_smoke(self): db_path = "test.sqlite" runner = CliRunner() From 7612302e62dfbc8406c3d04f2798a954d0ea69ac Mon Sep 17 00:00:00 2001 From: Tsuyoshi Hombashi Date: Sat, 29 Oct 2016 14:34:05 +0900 Subject: [PATCH 4/8] Update badges --- docs/pages/introduction/badges.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/pages/introduction/badges.txt b/docs/pages/introduction/badges.txt index 2f61a2a..a2a1f68 100644 --- a/docs/pages/introduction/badges.txt +++ b/docs/pages/introduction/badges.txt @@ -1,6 +1,10 @@ .. image:: https://img.shields.io/pypi/pyversions/sqlitebiter.svg :target: https://pypi.python.org/pypi/sqlitebiter -.. image:: https://travis-ci.org/thombashi/sqlitebiter.svg?branch=master + +.. image:: https://img.shields.io/travis/thombashi/sqlitebiter/master.svg?label=Linux :target: https://travis-ci.org/thombashi/sqlitebiter -.. image:: https://ci.appveyor.com/api/projects/status/hunqrvo1inm2jjnj?svg=true + :alt: Linux CI test status + +.. image:: https://img.shields.io/appveyor/ci/thombashi/sqlitebiter/master.svg?label=Windows :target: https://ci.appveyor.com/project/thombashi/sqlitebiter + :alt: Windows CI test status From d9d41fb876b61b7714b687766ea61fde18509070 Mon Sep 17 00:00:00 2001 From: Tsuyoshi Hombashi Date: Sun, 30 Oct 2016 00:36:10 +0900 Subject: [PATCH 5/8] Add support for Markdown tables --- requirements/requirements.txt | 5 +-- sqlitebiter/sqlitebiter.py | 65 ++++++++--------------------------- test/test_sqlitebiter.py | 24 +++++++++++-- 3 files changed, 40 insertions(+), 54 deletions(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index f6ec73c..e02fd54 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,5 +1,6 @@ click -DataProperty>=0.9.0 +DataProperty>=0.10.0 logbook path.py -SimpleSQLite>=0.5.5 +pytablereader>=0.2.0 +SimpleSQLite>=0.6.0 diff --git a/sqlitebiter/sqlitebiter.py b/sqlitebiter/sqlitebiter.py index f10b6a5..d997aae 100644 --- a/sqlitebiter/sqlitebiter.py +++ b/sqlitebiter/sqlitebiter.py @@ -6,18 +6,14 @@ """ from __future__ import absolute_import -import collections -import re import sys import click import dataproperty import logbook import path +import pytablereader as ptr import simplesqlite -from simplesqlite.loader import ValidationError -from simplesqlite.loader import InvalidDataError -from simplesqlite.loader import OpenError from ._counter import ResultCounter @@ -31,42 +27,6 @@ handler.push_application() -class LoaderNotFound(Exception): - pass - - -class LoaderFactory(object): - LoaderTuple = collections.namedtuple( - "LoaderTuple", "filename_regexp loader") - - __LOADERTUPLE_LIST = [ - LoaderTuple( - re.compile("[\.]csv$"), - simplesqlite.loader.CsvTableFileLoader()), - LoaderTuple( - re.compile("[\.]html$|[\.]htm$"), - simplesqlite.loader.HtmlTableFileLoader()), - LoaderTuple( - re.compile("[\.]json$"), - simplesqlite.loader.JsonTableFileLoader()), - LoaderTuple( - re.compile("[\.]xlsx$|[\.]xlsm$|[\.]xls$"), - simplesqlite.loader.ExcelTableFileLoader()), - ] - - @classmethod - def get_loader(cls, file_path): - for loadertuple in cls.__LOADERTUPLE_LIST: - if loadertuple.filename_regexp.search(file_path) is None: - continue - - loadertuple.loader.source = file_path - - return loadertuple.loader - - raise LoaderNotFound(file_path) - - def create_database(database_path): db_path = path.Path(database_path) dir_path = db_path.dirname() @@ -110,7 +70,7 @@ def cmd(ctx, log_level): @click.pass_context def file(ctx, files, output_path): """ - Convert CSV/Excel/HTML/JSON file(s) to a SQLite database file. + Convert CSV/Excel/HTML/Markdown/JSON file(s) to a SQLite database file. """ if dataproperty.is_empty_sequence(files): @@ -128,8 +88,13 @@ def file(ctx, files, output_path): continue try: - loader = LoaderFactory.get_loader(file_path) - except LoaderNotFound: + loader_factory = ptr.FileLoaderFactory(file_path) + except ptr.InvalidFilePathError: + continue + + try: + loader = loader_factory.create_from_file_path() + except ptr.LoaderNotFound: logger.debug( "loader not found that coincide with '{}'".format(file_path)) continue @@ -147,14 +112,14 @@ def file(ctx, files, output_path): click.echo("convert '{:s}' to '{:s}' table".format( file_path, tabledata.table_name)) - except OpenError as e: + except ptr.OpenError as e: logger.error(e) - except ValidationError as e: + except ptr.ValidationError as e: logger.error( "invalid {:s} data format: path={:s}, message={:s}".format( _get_format_type_from_path(file_path), file_path, str(e))) result_counter.inc_fail() - except InvalidDataError as e: + except ptr.InvalidDataError as e: logger.error( "invalid {:s} data: path={:s}, message={:s}".format( _get_format_type_from_path(file_path), file_path, str(e))) @@ -198,13 +163,13 @@ def gs(ctx, credentials, title, output_path): try: con.create_table_from_tabledata(tabledata) result_counter.inc_success() - except (ValidationError, InvalidDataError): + except (ptr.ValidationError, ptr.InvalidDataError): result_counter.inc_fail() - except OpenError as e: + except ptr.OpenError as e: logger.error(e) except AttributeError: logger.error("invalid credentials data: path={:s}".format(credentials)) - except (ValidationError, InvalidDataError) as e: + except (ptr.ValidationError, ptr.InvalidDataError) as e: logger.error( "invalid credentials data: path={:s}, message={:s}".format( credentials, str(e))) diff --git a/test/test_sqlitebiter.py b/test/test_sqlitebiter.py index de97bad..ef3d36d 100644 --- a/test/test_sqlitebiter.py +++ b/test/test_sqlitebiter.py @@ -11,7 +11,7 @@ import xlsxwriter from sqlitebiter.sqlitebiter import cmd -from simplesqlite.loader.interface import TableLoader +from pytablereader.interface import TableLoader def valid_json_single_file(): @@ -237,6 +237,19 @@ def invalid_html_file(): return file_path +def valid_markdown_file(): + file_path = "valid_mdtable.md" + with open(file_path, "w") as f: + f.write(""" a | b | c +--:|----:|--- + 1|123.1|a + 2| 2.2|bb + 3| 3.3|ccc +""") + + return file_path + + class Test_sqlitebiter: def setup_method(self, method): @@ -264,6 +277,7 @@ def test_normal_smoke(self): valid_csv_file2(), valid_excel_file(), valid_html_file(), + valid_markdown_file(), ] for file_path in file_list: @@ -320,6 +334,8 @@ def test_normal_multi(self): valid_html_file(), invalid_html_file(), + + valid_markdown_file(), ] result = runner.invoke(cmd, ["file"] + file_list + ["-o", db_path]) @@ -331,6 +347,7 @@ def test_normal_multi(self): 'csv_a', "insert_csv", 'excel_sheet_a', 'excel_sheet_c', 'excel_sheet_d', 'htmltable_tablename', 'htmltable_html2', + 'valid_mdtable_markdown1', ] message = "expected-tables={}, actual-tables={}".format( @@ -346,7 +363,8 @@ def test_normal_multi(self): "multijson_table2": [(1, '4'), (2, 'NULL'), (3, '120.9')], "csv_a": [(1, 4.0, 'a'), (2, 2.1, 'bb'), (3, 120.9, 'ccc')], - "insert_csv": [(1, 4.0, 'a'), (2, 2.1, 'bb'), (3, 120.9, 'ccc')], + "insert_csv": + [(1, 4.0, 'a'), (2, 2.1, 'bb'), (3, 120.9, 'ccc')], "excel_sheet_a": [(1.0, 1.1, 'a'), (2.0, 2.2, 'bb'), (3.0, 3.3, 'cc')], "excel_sheet_c": @@ -357,6 +375,8 @@ def test_normal_multi(self): [(1, 123.1, 'a'), (2, 2.2, 'bb'), (3, 3.3, 'ccc')], "htmltable_html2": [(1, 123.1), (2, 2.2), (3, 3.3)], + "valid_mdtable_markdown1": + [(1, 123.1, 'a'), (2, 2.2, 'bb'), (3, 3.3, 'ccc')], } for table in con.get_table_name_list(): result = con.select("*", table_name=table) From e68e0c2ae47dbe9a00d197692418336f8a809ec4 Mon Sep 17 00:00:00 2001 From: Tsuyoshi Hombashi Date: Sun, 30 Oct 2016 00:36:28 +0900 Subject: [PATCH 6/8] update docs --- docs/pages/installation.rst | 2 ++ docs/pages/introduction/feature.txt | 4 +++- docs/pages/introduction/summary.txt | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/pages/installation.rst b/docs/pages/installation.rst index b469fc7..8ba84dd 100644 --- a/docs/pages/installation.rst +++ b/docs/pages/installation.rst @@ -23,8 +23,10 @@ Dependency python packages are automatically installed during - `click `__ - `DataPropery `__ - `path.py `__ +- `pytablereader `__ - `SimpleSQLite `__ + Google Sheets dependencies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/pages/introduction/feature.txt b/docs/pages/introduction/feature.txt index ea23b76..92f4553 100644 --- a/docs/pages/introduction/feature.txt +++ b/docs/pages/introduction/feature.txt @@ -3,6 +3,8 @@ Features - Create a SQLite database file from: - CSV file(s) - - JSON file(s) - Microsoft Excel :superscript:`TM` file(s) + - HTML file(s): extract table tag data + - JSON file(s) + - Markdown file(s): extract Markdown table - `Google Sheets `_ diff --git a/docs/pages/introduction/summary.txt b/docs/pages/introduction/summary.txt index edba12d..f4d8bcd 100644 --- a/docs/pages/introduction/summary.txt +++ b/docs/pages/introduction/summary.txt @@ -1 +1 @@ -sqlitebiter is a CLI tool to convert CSV/Excel/HTML/JSON/Google-Sheets to a SQLite database file. +sqlitebiter is a CLI tool to convert CSV/Excel/HTML/JSON/Markdown/Google-Sheets to a SQLite database file. From 8e342743fa7f320460851ceeeecaa6a691cbd57a Mon Sep 17 00:00:00 2001 From: Tsuyoshi Hombashi Date: Sun, 30 Oct 2016 00:37:36 +0900 Subject: [PATCH 7/8] Update README --- README.rst | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index ed33b49..3d3da4a 100644 --- a/README.rst +++ b/README.rst @@ -3,23 +3,29 @@ sqlitebiter .. image:: https://img.shields.io/pypi/pyversions/sqlitebiter.svg :target: https://pypi.python.org/pypi/sqlitebiter -.. image:: https://travis-ci.org/thombashi/sqlitebiter.svg?branch=master + +.. image:: https://img.shields.io/travis/thombashi/sqlitebiter/master.svg?label=Linux :target: https://travis-ci.org/thombashi/sqlitebiter -.. image:: https://ci.appveyor.com/api/projects/status/hunqrvo1inm2jjnj?svg=true + :alt: Linux CI test status + +.. image:: https://img.shields.io/appveyor/ci/thombashi/sqlitebiter/master.svg?label=Windows :target: https://ci.appveyor.com/project/thombashi/sqlitebiter + :alt: Windows CI test status Summary ------- -sqlitebiter is a CLI tool to convert CSV/Excel/HTML/JSON/Google-Sheets to a SQLite database file. +sqlitebiter is a CLI tool to convert CSV/Excel/HTML/JSON/Markdown/Google-Sheets to a SQLite database file. Features -------- - Create a SQLite database file from: - CSV file(s) - - JSON file(s) - Microsoft Excel :superscript:`TM` file(s) + - HTML file(s): extract table tag data + - JSON file(s) + - Markdown file(s): extract Markdown table - `Google Sheets `_ Usage @@ -58,8 +64,10 @@ Dependency python packages are automatically installed during - `click `__ - `DataPropery `__ - `path.py `__ +- `pytablereader `__ - `SimpleSQLite `__ + Google Sheets dependencies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 31160213067825f1b27d3cda69c380dc174cb972 Mon Sep 17 00:00:00 2001 From: Tsuyoshi Hombashi Date: Sun, 30 Oct 2016 00:37:48 +0900 Subject: [PATCH 8/8] Bump version --- sqlitebiter/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlitebiter/__init__.py b/sqlitebiter/__init__.py index 91ee717..867e4b1 100644 --- a/sqlitebiter/__init__.py +++ b/sqlitebiter/__init__.py @@ -1 +1 @@ -VERSION = "0.2.1" +VERSION = "0.3.0"