Skip to content

Commit

Permalink
Merge decaabf into 632a19b
Browse files Browse the repository at this point in the history
  • Loading branch information
slint committed Feb 13, 2019
2 parents 632a19b + decaabf commit 16dbeb6
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 6 deletions.
1 change: 1 addition & 0 deletions .editorconfig
Expand Up @@ -23,6 +23,7 @@ indent_size = 4
known_first_party = invenio_sipstore
multi_line_output = 2
default_section = THIRDPARTY
skip = .eggs

# RST files (used by sphinx)
[*.rst]
Expand Down
13 changes: 11 additions & 2 deletions invenio_sipstore/api.py
Expand Up @@ -205,7 +205,7 @@ def sip(self):

@classmethod
def create(cls, pid, record, archivable, create_sip_files=True,
user_id=None, agent=None):
user_id=None, agent=None, sip_metadata_type=None):
"""Create a SIP, from the PID and the Record.
Apart from the SIP itself, it also creates ``RecordSIP`` for the
Expand All @@ -223,11 +223,20 @@ def create(cls, pid, record, archivable, create_sip_files=True,
:param bool archivable: tells if the record should be archived.
Usefull when ``Invenio-Archivematica`` is installed.
:param bool create_sip_files: If True the SIPFiles will be created.
:param str sip_metadata_type: Used to fetch the
:py:class:`invenio_sipstore.models.SIPMetadataType` by ``name``.
If not provided, the ``$schema`` attribute of ``record`` will be
used to determine the
:py:class:`invenio_sipstore.models.SIPMetadataType`.
(default: ``None``)
:returns: RecordSIP object.
:rtype: :py:class:`invenio_sipstore.api.RecordSIP`
"""
files = record.files if create_sip_files else None
mtype = SIPMetadataType.get_from_schema(record['$schema'])
if sip_metadata_type:
mtype = SIPMetadataType.get_from_name(sip_metadata_type)
else:
mtype = SIPMetadataType.get_from_schema(record['$schema'])
metadata = {mtype.name: json.dumps(record.dumps())}
with db.session.begin_nested():
sip = SIP.create(archivable, files=files, metadata=metadata,
Expand Down
33 changes: 32 additions & 1 deletion invenio_sipstore/archivers/bagit_archiver.py
Expand Up @@ -16,6 +16,8 @@
from flask import current_app
from invenio_db import db
from jsonschema import validate
from six import string_types
from werkzeug.utils import import_string

from invenio_sipstore.api import SIP
from invenio_sipstore.archivers import BaseArchiver
Expand Down Expand Up @@ -172,6 +174,26 @@ def _generate_bagging_date(self):
"""Generate the bagging date timestamp."""
return datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S.%f")

@staticmethod
def _generate_agent_tags(agent):
"""Generate the agent info tags.
This method can be changed in the config to suit your needs, see
:py:data:`invenio_sipstore.config.SIPSTORE_AGENT_TAGS_FACTORY`
:return: agent information tags.
:rtype: list[(str, str)]
"""
def _convert_key(key):
"""Convert from snake_case."""
return ('-').join([x.capitalize() for x in key.split('_')])

exclude = ['$schema']

return [('X-Agent-{0}'.format(_convert_key(k)), v)
for k, v in sorted(agent.items())
if isinstance(v, string_types) and k not in exclude]

def get_baginfo_file(self, filesinfo):
"""Create the bag-info.txt file from the tags."""
# Include some auto-generated tags
Expand All @@ -185,6 +207,15 @@ def get_baginfo_file(self, filesinfo):
t_value = '{0}/{1}'.format(self.sip.id, self.archiver_version)
content.append("{0}: {1}".format(t_name, t_value))

# Include agent tags
if self.sip.agent:
agent_tags_factory = import_string(
current_app.config['SIPSTORE_AGENT_TAGS_FACTORY'])
agent_tags = agent_tags_factory(self.sip.agent)

for k, v in agent_tags:
content.append("{0}: {1}".format(k, v))

content = "\n".join(content)
return self._generate_extra_info(content, 'bag-info.txt')

Expand Down Expand Up @@ -243,7 +274,7 @@ def get_all_files(self):
extra_files = self._get_extra_files(data_files, metadata_files)

bagit_files = []
fetched_fi = [fi for fi in data_files if self._is_fetched(fi)]
fetched_fi = [f for f in data_files if self._is_fetched(f)]
if fetched_fi:
bagit_files.append(self.get_fetch_file(fetched_fi))
bagit_files.append(self.get_baginfo_file(data_files + metadata_files +
Expand Down
4 changes: 4 additions & 0 deletions invenio_sipstore/config.py
Expand Up @@ -24,6 +24,10 @@
SIPSTORE_AGENT_FACTORY = 'invenio_sipstore.api.SIP._build_agent_info'
"""Factory to build the agent, stored for the information about the SIP."""

SIPSTORE_AGENT_TAGS_FACTORY = \
'invenio_sipstore.archivers.BagItArchiver._generate_agent_tags'
"""Factory to build agent information tags."""

SIPSTORE_FILEPATH_MAX_LEN = 1024
"""Max filepath length."""

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -24,7 +24,7 @@
'pytest-cov>=1.8.0',
'pytest-mock>=1.6.0',
'pytest-pep8>=1.0.6',
'pytest>=2.8.0',
'pytest>=3.6.0',
]

extras_require = {
Expand Down
7 changes: 6 additions & 1 deletion tests/conftest.py
Expand Up @@ -146,7 +146,12 @@ def sips(db, locations, sip_metadata_types):
SIP-3: File2(renamed on SIPFile, but same FileInstance), File3
SIP-4: File4, File5, File6
"""
sip1 = SIP.create()
# A SIP with agent info
sip1 = SIP.create(agent={
'email': 'spiderpig@invenio.org',
'orcid': '1111-1111-1111-1111',
'ip_address': '1.1.1.1'
})
sip1api = SIPApi(sip1)
sip1api.attach_metadata('marcxml-test', '<p>XML 1</p>')
sip1api.attach_metadata('json-test', '{"title": "JSON 1"}')
Expand Down
14 changes: 14 additions & 0 deletions tests/test_api.py
Expand Up @@ -250,5 +250,19 @@ def test_RecordSIP_create(db, mocker):
assert SIPMetadata.query.count() == 2
assert len(rsip.sip.files) == 0
assert len(rsip.sip.metadata) == 1

# try with specific SIP metadata type
mtype = SIPMetadataType(title='JSON Test 2', name='json-test-2',
format='json', schema=None) # no schema
db.session.add(mtype)
db.session.commit()

rsip = RecordSIP.create(pid, record, True, create_sip_files=False,
user_id=user.id, agent=agent,
sip_metadata_type='json-test-2')
assert SIPMetadata.query.count() == 3
assert len(rsip.sip.metadata) == 1
assert rsip.sip.metadata[0].type.id == mtype.id

# finalization
rmtree(tmppath)
5 changes: 4 additions & 1 deletion tests/test_bagit_archiver.py
Expand Up @@ -193,7 +193,10 @@ def test_write_patched(mocker, sips, archive_fs,
"Bagging-Date: {0}\n".format(dt) +
"Payload-Oxum: 93.4\n"
"External-Identifier: {0}/SIPBagIt-v1.0.0\n".format(sips[0].id) +
"External-Description: BagIt archive of SIP."
"External-Description: BagIt archive of SIP.\n"
"X-Agent-Email: spiderpig@invenio.org\n"
"X-Agent-Ip-Address: 1.1.1.1\n"
"X-Agent-Orcid: 1111-1111-1111-1111"
)),
]
expected_sip2 = [
Expand Down

0 comments on commit 16dbeb6

Please sign in to comment.