Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add to_json method for exporting Entry metadata to JSON #306

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions metacatalog/models/entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from uuid import uuid4
import warnings
from collections import defaultdict
from decimal import Decimal

from sqlalchemy import Column, ForeignKey, event
from sqlalchemy import Integer, String, Boolean, DateTime
Expand Down Expand Up @@ -399,10 +400,41 @@ def from_dict(cls, session: Session, data: dict) -> 'Entry':

return entry


@classmethod
def is_valid(cls, entry: 'Entry') -> bool:
return isinstance(entry, Entry) and entry.id is not None


def to_json(self, path: str) -> None:
"""
Exports the Entry metadata (from the to_dict method) to a serializable
JSON.

.. versionadded:: 0.9.1

Parameters
----------
path : str
Path to the file to write the JSON to.

"""
# custom encoder for datetime and Decimal data types
class DictToSerializableJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Decimal):
return str(obj)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would make more sense to convert the Decimal to a float, right?

if isinstance(obj, dt):
return obj.isoformat()
return super().default(obj)
Comment on lines +423 to +429
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's define this in some kind of utility file, so that other models can use it as well.


# get the dict
entry_dict = self.to_dict()

# write
with open(path, 'w') as f:
json.dump(entry_dict, f, indent=4, cls=DictToSerializableJsonEncoder)
Comment on lines +435 to +436
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok.

Other option is to keep the writing to file out of meta catalog and implement the class directly into the tool, where it is used. What do you think?


@property
def checksum(self) -> str:
"""
Expand Down
33 changes: 32 additions & 1 deletion metacatalog/test/test_todict_fromdict.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from metacatalog import api
from metacatalog.models import Entry
from ._util import connect
import json


def check_to_dict_persons(session):
Expand Down Expand Up @@ -46,8 +47,37 @@ def check_from_dict(session):
return True


def check_to_json(session, tmp_path):
"""
Check Entry.to_json().
This also checks if the output is a valid json by trying to load
it with json.load().

"""
# test for all entries in test database
for entry in api.find_entry(session):
# create a temporary file
tmp_file = tmp_path / "entry.json"

# write JSON to the temporary file
entry.to_json(path=str(tmp_file))

# load the JSON from the file, if the JSON is not valid, the test fails with a JSONDecodeError

with open(tmp_file, 'r') as f:
loaded_dict = json.load(f)

assert loaded_dict['title'] == entry.title
assert isinstance(loaded_dict['id'], int)
assert isinstance(loaded_dict['author'], dict)
assert isinstance(loaded_dict['authors'], list)
assert isinstance(loaded_dict['embargo'], bool)

return True


@pytest.mark.depends(on=['add_find'], name='dict_methods')
def test_fromdict_todict():
def test_fromdict_todict(tmp_path):
"""
Currently tests Entry.to_dict() and Entry.from_dict()
"""
Expand Down Expand Up @@ -75,3 +105,4 @@ def test_fromdict_todict():
# run single tests
assert check_to_dict_persons(session)
assert check_from_dict(session)
assert check_to_json(session, tmp_path=tmp_path)
Loading