-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* change!(db.find): pass key, value into lambda function * feat: create `Condition` to find method * feat(Condition): supports `AND`, `OR` * change!(db.find): restore not to pass object id as key * fix(Condition): compare with None * test(db.find): add unit tests * change exporting module defines * docs: update `Database.find()` * refactor: pull method up into interface * refactor: move method into util module * refactor: reorganize module directories * feat(Database): beautify representation * feat: add decorater to document * fix: add missing dict method * tests: add unit tests for dictionary methods * tests: add `fail()` method * docs: update changes * refactor(tests): rename methods * fix(database): getter with list * docs: update changes and example * feat: add static method `load` (#7) * docs: update changes * fix: change `__version__` to constant value * version up to 0.2.4 * docs: fix wrong urls * build: add more trigger types
- Loading branch information
Showing
19 changed files
with
392 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,13 @@ | ||
from .core.database import Database | ||
from .core.matcher import Condition, Conditions, Key | ||
|
||
__version__ = '0.2.4' | ||
from .statics import load | ||
from .constants import __version__ | ||
|
||
__all__ = [ | ||
"__version__", | ||
"Condition", | ||
"Conditions", | ||
"Database", | ||
"Key", | ||
"load", | ||
] |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from typing import Callable | ||
|
||
|
||
def copy_doc(original: Callable) -> Callable: | ||
def wrapper(target: Callable) -> Callable: | ||
target.__doc__ = original.__doc__ | ||
return target | ||
return wrapper |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
package_name = "json_as_db" | ||
|
||
__version__ = '0.2.4' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
from typing import List, Tuple | ||
|
||
|
||
def split_first_last(l: list, k: int) -> Tuple[list, list]: | ||
_len = len(l) | ||
if _len <= 1: | ||
return l, [] | ||
is_odd = k % 2 | ||
k = min(k // 2, _len // 2) | ||
return l[:(k+int(is_odd))], l[-k:] | ||
|
||
|
||
def to_plural(unit_str: str, k: int) -> str: | ||
return unit_str + ('' if k < 2 else 's') | ||
|
||
|
||
def collapsed_row(l: list, is_skip: bool) -> list: | ||
_first, _last = split_first_last(l, len(l)) | ||
return _first + (['...'] if is_skip else []) + _last | ||
|
||
|
||
def collapse_str(s: str, width: int) -> str: | ||
if len(s) <= width: | ||
return s | ||
return s[:min(len(s), width - 3)] + '...' | ||
|
||
|
||
def row_str(l: list, delimiter: str = '') -> str: | ||
return f" {delimiter} ".join(l) + "\n" | ||
|
||
|
||
def row_padded(row: list, widths: list) -> List[str]: | ||
return [row[i].ljust(widths[i]) for i in range(len(row))] | ||
|
||
|
||
def stringify(all_items: List[dict]) -> str: | ||
"""Return 3 each rows from the top and the bottom. | ||
Returns: | ||
str: The first and last 3 rows of the caller object. | ||
Example: | ||
>>> db | ||
age grouped ... job name | ||
32 True ... Camera operator Layne | ||
17 False ... Flying instructor Somerled | ||
9 True ... Inventor Joon-Ho | ||
... ... ... ... ... | ||
23 None ... Publican Melanie | ||
54 True ... Racing driver Eike | ||
41 None ... Barrister Tanja | ||
[100 items, 9 keys] | ||
""" | ||
# Collect key names to be column | ||
keys = set() | ||
for item in all_items: | ||
keys |= set(item.keys()) | ||
total_rows = len(all_items) | ||
total_cols = len(keys) | ||
|
||
# Display options | ||
keys = sorted(list(keys)) | ||
rows_display = 6 | ||
cols_display = 4 | ||
text_max_width = 12 | ||
|
||
# Collect rows by columns and collapse them | ||
first_cols, last_cols = split_first_last(keys, cols_display) | ||
first_rows, last_rows = split_first_last(all_items, rows_display) | ||
rows = first_rows + last_rows | ||
cols = first_cols + last_cols | ||
col_widths = [max(3, len(str(col))) for col in cols] | ||
table = [] | ||
for irow, row in enumerate(rows): | ||
t_row = [] | ||
for icol, col in enumerate(cols): | ||
try: | ||
stringified = str(row[col]) | ||
except KeyError: | ||
stringified = str(None) | ||
text = collapse_str(stringified, width=text_max_width) | ||
col_widths[icol] = max(col_widths[icol], len(text)) | ||
t_row.append(text) | ||
table.append(t_row) | ||
|
||
# Shortcut functions | ||
def _clp_row(l): return collapsed_row(l, is_skip=total_cols > cols_display) | ||
def _make_row_str(l): return row_str(_clp_row(l), delimiter='') | ||
def _padded(l): return row_padded(l, widths=col_widths) | ||
|
||
# Create result strings | ||
result = _make_row_str(_padded(first_cols + last_cols)) | ||
for irow, row in enumerate(table): | ||
s = _padded(row) | ||
result += _make_row_str(s) | ||
if total_rows > rows_display and irow + 1 == len(first_rows): | ||
result += _make_row_str(_padded(['...'] * (len(cols)))) | ||
|
||
result += "\n\n" + ", ".join([ | ||
f"[{total_rows} {to_plural('item', total_rows)}", | ||
f"{total_cols} {to_plural('key', total_cols)}]", | ||
]) | ||
|
||
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from .core.database import Database | ||
|
||
|
||
def load(path: str, *args, **kwargs) -> Database: | ||
return Database().load(path, *args, **kwargs) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import os | ||
import pytest | ||
|
||
from utils import file, logger, fail | ||
|
||
from json_as_db import Database | ||
|
||
|
||
CUR_DIR = os.path.dirname(os.path.realpath(__file__)) | ||
DB_FILENAME = 'db.json' | ||
DB_FILEPATH = os.path.join(CUR_DIR, '..', 'samples', DB_FILENAME) | ||
REC_ID = 'kcbPuqpfV3YSHT8YbECjvh' | ||
REC_ID_2 = 'jmJKBJBAmGESC3rGbSb62T' | ||
REC_ID_NOT_EXIST = 'N0t3xIstKeyV41ueString' | ||
DB_STR_OUTPUT = """booleanFalse booleanTrue ... randomInteger randomString | ||
False True ... 123 keyboard-cat | ||
None None ... 321 cheshire-cat | ||
[2 items, 6 keys]""" | ||
|
||
|
||
@pytest.fixture() | ||
def db() -> Database: | ||
return Database().load(DB_FILEPATH) | ||
|
||
|
||
def test_getter(db: Database): | ||
item = db[REC_ID] | ||
logger.debug(item) | ||
assert type(item) is dict | ||
assert item['randomInteger'] == 123 | ||
assert type(db[REC_ID_NOT_EXIST]) is type(None) | ||
|
||
|
||
def test_getter_by_list(db: Database): | ||
items = db[[REC_ID, REC_ID_2]] | ||
logger.debug(items) | ||
assert type(items) is list | ||
assert len(items) == 2 | ||
items = db[[REC_ID_NOT_EXIST]] | ||
assert items == [None] | ||
|
||
|
||
def test_setter(db: Database): | ||
assert db[REC_ID]['randomInteger'] == 123 | ||
try: | ||
db[REC_ID] = {'test': True} | ||
except NotImplementedError: | ||
pass | ||
except: | ||
fail() | ||
|
||
|
||
def test_del(db: Database): | ||
try: | ||
del db[REC_ID] | ||
except: | ||
fail() | ||
assert db[REC_ID] is None | ||
|
||
|
||
def test_len(db: Database): | ||
assert type(len(db)) is int | ||
assert len(db) == 2 | ||
|
||
|
||
def test_items(db: Database): | ||
assert type(db.items()) is type(dict().items()) | ||
|
||
|
||
def test_keys(db: Database): | ||
assert type(db.keys()) is type(dict().keys()) | ||
|
||
|
||
def test_values(db: Database): | ||
assert type(db.values()) is type(dict().values()) | ||
|
||
|
||
def test_repr(db: Database): | ||
assert repr(db) == DB_STR_OUTPUT | ||
|
||
|
||
def test_str(db: Database): | ||
assert str(db) == DB_STR_OUTPUT | ||
|
||
|
||
def test_contains(db: Database): | ||
"""The `in` keyword is used to check if a value is present in a sequence (list, range, string etc.). | ||
""" | ||
assert True == (REC_ID in db) | ||
assert True == (REC_ID_2 in db) | ||
assert False == (REC_ID_NOT_EXIST in db) | ||
|
||
|
||
def test_constructor(): | ||
extenral_dict = dict(age=25, name='Tom') | ||
try: | ||
db = Database(extenral_dict) | ||
except: | ||
fail() | ||
|
||
|
||
def test_deconstructor(): | ||
db = Database(dict()) | ||
try: | ||
del db | ||
except: | ||
fail() |
Oops, something went wrong.