Пример #1
0
def test_rest_delete_record(app, db, es, es_acl_prepare, test_users):
    with app.test_client() as client:
        with db.session.begin_nested():
            acl = DefaultACL(name='default',
                             schemas=[RECORD_SCHEMA],
                             priority=0,
                             originator=test_users.u1,
                             operation='update')
            actor = UserActor(name='u1',
                              users=[test_users.u1],
                              acl=acl,
                              originator=test_users.u1)

            db.session.add(acl)
            db.session.add(actor)

        pid, record = create_record({'keywords': ['blah']},
                                    clz=SchemaEnforcingRecord)
        RecordIndexer().index(record)
        current_search_client.indices.refresh()
        current_search_client.indices.flush()

        login(client, test_users.u2)
        response = client.delete(record_url(pid))
        assert response.status_code == 403

        login(client, test_users.u1)
        response = client.delete(record_url(pid))
        assert response.status_code == 204

        with pytest.raises(NoResultFound):
            Record.get_record(pid.object_uuid)
def test_valid_delete(app):
    """Test VALID record delete request (DELETE .../records/<record_id>)."""
    with app.app_context():
        # create the record using the internal API
        pid, record = create_record(test_data)

        with app.test_client() as client:
            headers = [('Accept', 'application/json')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 204
            # check database state
            with pytest.raises(NoResultFound):
                Record.get_record(record.id)
            assert pid.is_deleted()

            # check that DELETE with non JSON accepted format will work
            # as it returns nothing
            pid, record = create_record(test_data)
            headers = [('Accept', 'video/mp4')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 204
Пример #3
0
def test_valid_delete(app):
    """Test VALID record delete request (DELETE .../records/<record_id>)."""
    with app.app_context():
        # create the record using the internal API
        pid, record = create_record(test_data)

        with app.test_client() as client:
            headers = [('Accept', 'application/json')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 204
            # check database state
            with pytest.raises(NoResultFound):
                Record.get_record(record.id)
            assert pid.is_deleted()

            # check that DELETE with non JSON accepted format will work
            # as it returns nothing
            pid, record = create_record(test_data)
            headers = [('Accept', 'video/mp4')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 204
Пример #4
0
def test_record_update_mutable(app, db):
    """Test updating mutables in a record."""
    recid = uuid.UUID("262d2748-ba41-456f-a844-4d043a419a6f")

    # Create a new record with two mutables, a list and a dict
    rec = Record.create({"title": "Title", "list": ["foo"], "dict": {"moo": "boo"}}, id_=recid)
    # Make sure mutables are there before and after commit
    assert rec == {"title": "Title", "list": ["foo"], "dict": {"moo": "boo"}}
    db.session.commit()
    db.session.expunge_all()
    rec = Record.get_record(recid)
    assert rec == {"title": "Title", "list": ["foo"], "dict": {"moo": "boo"}}

    # Set the mutables under key
    rec["list"] = ["bar"]
    rec["dict"] = {"eggs": "bacon"}
    rec.commit()
    # Make sure it commits to DB
    assert rec == {"title": "Title", "list": ["bar"], "dict": {"eggs": "bacon"}}
    db.session.commit()
    db.session.expunge_all()
    rec = Record.get_record(recid)
    assert rec == {"title": "Title", "list": ["bar"], "dict": {"eggs": "bacon"}}

    # Update the mutables under key
    rec["list"].append("spam")
    rec["dict"]["ham"] = "chicken"
    rec.commit()
    # Make sure it commits to DB
    assert rec == {"title": "Title", "list": ["bar", "spam"], "dict": {"eggs": "bacon", "ham": "chicken"}}
    db.session.commit()
    db.session.expunge_all()
    rec = Record.get_record(recid)
    assert rec == {"title": "Title", "list": ["bar", "spam"], "dict": {"eggs": "bacon", "ham": "chicken"}}
Пример #5
0
def test_metadata_extraction_video(app, db, cds_depid, bucket, video):
    """Test metadata extraction video mp4."""
    recid = PersistentIdentifier.get('depid', cds_depid).object_uuid
    # simulate a no fully filled record
    record = Record.get_record(recid)
    if 'title' in record:
        del record['title']
    validator = 'cds.modules.records.validators.PartialDraft4Validator'
    with pytest.raises(ValidationError):
        record.commit()
    record.commit(validator=import_string(validator))

    # Extract metadata
    obj = ObjectVersion.create(bucket=bucket, key='video.mp4')
    obj_id = str(obj.version_id)
    dep_id = str(cds_depid)
    task_s = ExtractMetadataTask().s(uri=video,
                                     version_id=obj_id,
                                     deposit_id=dep_id)
    task_s.delay()

    # Check that deposit's metadata got updated
    record = Record.get_record(recid)
    assert 'extracted_metadata' in record['_cds']
    assert record['_cds']['extracted_metadata']

    # Check that ObjectVersionTags were added
    tags = ObjectVersion.query.first().get_tags()
    assert tags['duration'] == '60.095000'
    assert tags['bit_rate'] == '612177'
    assert tags['avg_frame_rate'] == '288000/12019'
    assert tags['codec_name'] == 'h264'
    assert tags['codec_long_name'] == 'H.264 / AVC / MPEG-4 AVC / ' \
                                      'MPEG-4 part 10'
    assert tags['width'] == '640'
    assert tags['height'] == '360'
    assert tags['nb_frames'] == '1440'
    assert tags['display_aspect_ratio'] == '16:9'
    assert tags['color_range'] == 'tv'

    # Undo
    ExtractMetadataTask().clean(deposit_id=dep_id, version_id=obj_id)

    # Check that deposit's metadata got reverted
    record = Record.get_record(recid)
    assert 'extracted_metadata' not in record['_cds']

    # Check that ObjectVersionTags are still there (attached to the old obj)
    tags = ObjectVersion.query.first().get_tags()
    assert len(tags) == 11

    # Simulate failed task, no extracted_metadata
    ExtractMetadataTask().clean(deposit_id=dep_id, version_id=obj_id)
    record = Record.get_record(recid)
    assert 'extracted_metadata' not in record['_cds']

    # check again tags
    tags = ObjectVersion.query.first().get_tags()
    assert len(tags) == 11
def test_db():
    """Test database backend."""
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get(
        'SQLALCHEMY_DATABASE_URI', 'sqlite:///test.db'
    )
    FlaskCLI(app)
    InvenioDB(app)
    InvenioRecords(app)

    with app.app_context():
        create_database(db.engine.url)
        db.create_all()
        assert len(db.metadata.tables) == 3

    data = {'title': 'Test'}
    from invenio_records.models import RecordMetadata as RM

    # Create a record
    with app.app_context():
        assert RM.query.count() == 0

        record_uuid = Record.create(data).id
        db.session.commit()

        assert RM.query.count() == 1
        db.session.commit()

    # Retrieve created record
    with app.app_context():
        record = Record.get_record(record_uuid)
        assert record.dumps() == data
        with pytest.raises(NoResultFound):
            Record.get_record(uuid.uuid4())
        record['field'] = True
        record = record.patch([
            {'op': 'add', 'path': '/hello', 'value': ['world']}
        ])
        assert record['hello'] == ['world']
        record.commit()
        db.session.commit()

    with app.app_context():
        record2 = Record.get_record(record_uuid)
        assert record2.model.version_id == 2
        assert record2['field']
        assert record2['hello'] == ['world']
        db.session.commit()

    # Cannot commit record without model (i.e. Record.create_record)
    with app.app_context():
        record3 = Record({'title': 'Not possible'})
        with pytest.raises(RecordNotCommitableError):
            record3.commit()

    with app.app_context():
        db.drop_all()
        drop_database(db.engine.url)
Пример #7
0
def acl_changed_reindex(acl_id):
    """
    ACL has been changed so reindex all the documents in the given index.

    :param acl_id:   id of ACL instance
    """
    logger.info('Reindexing started for ACL=%s', acl_id)

    timestamp = datetime.datetime.now().astimezone().isoformat()

    acl = ACL.query.filter_by(id=acl_id).one_or_none()

    if not acl:
        # deleted in the meanwhile, so just return
        return  # pragma no cover

    # make sure all indices are flushed so that no resource is obsolete in index
    for schema in acl.schemas:
        current_search_client.indices.flush(index=schema_to_index(schema)[0])

    indexer = RecordIndexer()
    updated_count = 0
    removed_count = 0

    for id in acl.get_matching_resources():
        try:
            rec = Record.get_record(id)
        except:  # pragma no cover
            # record removed in the meanwhile by another thread/process,
            # indexer should have been called to remove it from ES
            # won't test this so pragma no cover
            continue
        try:
            indexer.index(rec)
            updated_count += 1
        except Exception as e:  # pragma no cover
            logger.exception('Error indexing ACL for resource %s: %s', id, e)

    # reindex the resources those were indexed by this acl but no longer should be
    for id in acl.used_in_records(older_than_timestamp=timestamp):
        try:
            rec = Record.get_record(id)
        except NoResultFound:  # pragma no cover
            continue
        except:  # pragma no cover
            logger.exception('Unexpected exception in record reindexing')
            continue

        try:
            removed_count += 1
            indexer.index(rec)
        except:  # pragma no cover
            logger.exception('Error indexing ACL for obsolete resource %s', id)

    logger.info(
        'Reindexing finished for ACL=%s, acl applied to %s records, acl removed from %s records',
        acl_id, updated_count, removed_count)
Пример #8
0
def test_record_deletion(test_record):
    recid = test_record.get('control_number')
    assert recid

    test_record.delete()
    db.session.commit()

    pid = PersistentIdentifier.get('recid', recid)
    assert pid.status == PIDStatus.DELETED

    with raises(NoResultFound):
        Record.get_record(pid.object_uuid)
Пример #9
0
def test_change_record_community(app, test_records):
    """Test updating the community field fails."""
    with app.app_context():
        record = Record.get_record(test_records[0].record_id)
        del record['community']
        with pytest.raises(AlteredRecordError):
            record.commit()

    with app.app_context():
        record = Record.get_record(test_records[0].record_id)
        record['community'] = str(uuid.uuid4())
        with pytest.raises(AlteredRecordError):
            record.commit()
Пример #10
0
def revert_delete_record(pid):
    """Reverts deletion action of a record."""
    pid_obj = PersistentIdentifier.query.filter_by(pid_value=pid).one()

    record_uuid = pid_obj.object_uuid
    deleted = Record.get_record(record_uuid, with_deleted=True)

    if not deleted.is_deleted:
        click.secho(f"Record {pid} was not deleted. Aborting.", fg="red")
        return

    # revert to previous revision
    record = deleted.revert(deleted.revision_id - 1)

    all_pid_objects = PersistentIdentifier.query.filter_by(
        object_uuid=record_uuid)

    # trying to get all the pid types (including legacy pids)
    for pid_object in all_pid_objects:
        pid_object.status = PIDStatus.REGISTERED

    db.session.commit()

    indexer_class = current_app_ils.indexer_by_pid_type(pid_obj.pid_type)
    indexer_class.index(record)
    click.secho(f"Record {pid} is reverted from deletion.", fg="green")
Пример #11
0
def test_record_patch_immutable_fields(app, test_records, test_users,
                                        login_user):
    """Test invalid modification of record draft with HTTP PATCH."""
    with app.app_context():
        record = Record.get_record(test_records[0].record_id)
        with app.test_client() as client:
            user = test_users['admin']
            login_user(user, client)

            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]

            for path in IMMUTABLE_PATHS:
                for command in [
                    {"op": "replace", "path": path, "value": ""},
                    {"op": "remove", "path": path},
                    {"op": "add", "path": path, "value": ""},
                    {"op": "copy", "from": "/title", "path": path, "value": ""},
                    {"op": "move", "from": "/title", "path": path, "value": ""},
                ]:
                    draft_patch_res = client.patch(
                        url_for('b2share_records_rest.b2rec_item',
                                pid_value=test_records[0].pid),
                        data=json.dumps([command]),
                        headers=headers)
                    assert draft_patch_res.status_code == 400
Пример #12
0
def test_record_patch_immutable_fields(app, test_records, test_users,
                                        login_user):
    """Test invalid modification of record draft with HTTP PATCH."""
    with app.app_context():
        record = Record.get_record(test_records[0].record_id)
        with app.test_client() as client:
            user = test_users['admin']
            login_user(user, client)

            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]

            for path in IMMUTABLE_PATHS:
                for command in [
                    {"op": "replace", "path": path, "value": ""},
                    {"op": "remove", "path": path},
                    {"op": "add", "path": path, "value": ""},
                    {"op": "copy", "from": "/title", "path": path, "value": ""},
                    {"op": "move", "from": "/title", "path": path, "value": ""},
                ]:
                    draft_patch_res = client.patch(
                        url_for('b2share_records_rest.b2rec_item',
                                pid_value=test_records[0].pid),
                        data=json.dumps([command]),
                        headers=headers)
                    assert draft_patch_res.status_code == 400
                    data = json.loads(draft_patch_res.get_data(as_text=True))
                    assert data['errors'][0]['message'] == \
                        'The field "{}" is immutable.'.format(path)
Пример #13
0
def test_metadata_extraction_video_mp4(app, db, depid, bucket, video_mp4):
    """Test metadata extraction video mp4."""
    # Extract metadata
    obj = ObjectVersion.create(bucket=bucket, key='video.mp4')
    video_metadata_extraction.s(uri=video_mp4,
                                object_version=str(obj.version_id),
                                deposit_id=str(depid)).delay()

    # Check that deposit's metadata got updated
    recid = PersistentIdentifier.get('depid', depid).object_uuid
    record = Record.get_record(recid)
    assert 'extracted_metadata' in record['_deposit']
    assert record['_deposit']['extracted_metadata']

    # Check that ObjectVersionTags were added
    obj = ObjectVersion.query.first()
    tags = obj.get_tags()
    assert tags['duration'] == '60.095000'
    assert tags['bit_rate'] == '612177'
    assert tags['size'] == '5510872'
    assert tags['avg_frame_rate'] == '288000/12019'
    assert tags['codec_name'] == 'h264'
    assert tags['width'] == '640'
    assert tags['height'] == '360'
    assert tags['nb_frames'] == '1440'
    assert tags['display_aspect_ratio'] == '0:1'
    assert tags['color_range'] == 'tv'
Пример #14
0
def _update_event_bucket(event):
    """Update event's payload with correct bucket of deposit."""
    depid = event.payload['deposit_id']
    dep_uuid = str(PersistentIdentifier.get('depid', depid).object_uuid)
    deposit_bucket = Record.get_record(dep_uuid)['_buckets']['deposit']
    event.payload['bucket_id'] = deposit_bucket
    flag_modified(event, 'payload')
Пример #15
0
def record_upsert(json):
    """Insert or update a record."""
    control_number = json.get('control_number', json.get('recid'))
    if control_number:
        control_number = int(control_number)
        pid_type = InspireRecordIdProvider.schema_to_pid_type(json['$schema'])
        try:
            pid = PersistentIdentifier.get(pid_type, control_number)
            record = Record.get_record(pid.object_uuid)
            record.update(json)
            record.commit()
        except PIDDoesNotExistError:
            record = Record.create(json, id_=None)
            # Create persistent identifier.
            inspire_recid_minter(str(record.id), json)

        if json.get('deleted'):
            new_recid = get_recid_from_ref(json.get('new_record'))
            if new_recid:
                merged_record = get_db_record(pid_type, new_recid)
                merge_pidstores_of_two_merged_records(merged_record.id, record.id)
            else:
                soft_delete_pidstore_for_record(record.id)

        return record
def test_valid_put(app):
    """Test VALID record patch request (PATCH .../records/<record_id>)."""
    with app.app_context():
        InvenioRecordsREST(app)
        # create the record using the internal API
        internal_record = Record.create(test_data)
        with app.test_client() as client:
            headers = [('Content-Type', 'application/json'),
                       ('Accept', 'application/json')]
            res = client.put(url_for(blueprint.name + '.' +
                                     RecordResource.view_name,
                                     record_id=internal_record.model.id),
                             data=json.dumps(test_data_patched),
                             headers=headers)
            assert res.status_code == 200
            # check that the returned record matches the given data
            response_data = json.loads(res.get_data(as_text=True))
            assert response_data['data'] == test_data_patched

            # check that an internal record returned id and that it contains
            # the same data
            assert 'id' in response_data.keys()
            internal_record = Record.get_record(response_data['id'])
            assert internal_record == response_data['data']

            # check that the returned self link returns the same data
            subtest_self_link(response_data, res.headers, client)
Пример #17
0
def test_change_record_schema_fails(app, test_records):
    """Test updating the $schema field fails."""
    with app.app_context():
        record = Record.get_record(test_records[0].record_id)
        del record['$schema']
        with pytest.raises(AlteredRecordError):
            record.commit()
Пример #18
0
def files_permission_factory(obj=None, action=None):
    """Permission for files are always based on the type of bucket.

    1. Record bucket: Read access only with open and restricted access.
    2. Any other bucket is restricted to admins only.
    """
    # Extract bucket id
    bucket_id = None
    if isinstance(obj, Bucket):
        bucket_id = str(obj.id)
    elif isinstance(obj, ObjectVersion):
        bucket_id = str(obj.bucket_id)
    elif isinstance(obj, MultipartObject):
        bucket_id = str(obj.bucket_id)
    elif isinstance(obj, FileObject):
        bucket_id = str(obj.bucket_id)

    # Retrieve record
    if bucket_id is not None:
        record_bucket = RecordsBuckets.query.filter_by(bucket_id=bucket_id).one_or_none()
        if record_bucket is not None:
            record = Record.get_record(record_bucket.record_id)

            return FilePermission.create(record, action)

    return AdminPermission.create(obj, action)
Пример #19
0
def run_task_on_referrers(reference, task, success_task=None, error_task=None):
    """
    Queues a task for all referrers referring the given reference.

    :param reference: reference for which to run the tasks on referrers
    :param task: a celery signature
    :param success_task: a celery signature to handle success of task chain
    :param error_task: a celery signature to handle error of a certain task
    """
    refs = current_references.get_records(reference)

    task_list = []
    rec_list = []

    for ref in refs:
        rec = Record.get_record(id_=ref.record.record_uuid)
        # Add the referencing record to the task signature
        record_task = task.clone(kwargs={'record': rec})
        if error_task:
            record_error = error_task.clone(kwargs={'record': rec})
            record_task = record_task.on_error(record_error)

        task_list.append(record_task)
        rec_list.append(rec)

    job = chain(*task_list)
    if success_task:
        job_result = job.apply_async(link=success_task.clone(
            kwargs={'records': rec_list}))
    else:
        job_result = job.apply_async()
    return job_result
Пример #20
0
def test_valid_put(app):
    """Test VALID record patch request (PATCH .../records/<record_id>)."""
    with app.app_context():
        InvenioRecordsREST(app)
        # create the record using the internal API
        internal_record = Record.create(test_data)
        with app.test_client() as client:
            headers = [('Content-Type', 'application/json'),
                       ('Accept', 'application/json')]
            res = client.put(url_for(blueprint.name + '.' +
                                     RecordResource.view_name,
                                     record_id=internal_record.model.id),
                             data=json.dumps(test_data_patched),
                             headers=headers)
            assert res.status_code == 200
            # check that the returned record matches the given data
            response_data = json.loads(res.get_data(as_text=True))
            assert response_data['data'] == test_data_patched

            # check that an internal record returned id and that it contains
            # the same data
            assert 'id' in response_data.keys()
            internal_record = Record.get_record(response_data['id'])
            assert internal_record == response_data['data']

            # check that the returned self link returns the same data
            subtest_self_link(response_data, res.headers, client)
Пример #21
0
    def test_record_crud_2(self, load_entry_points, app, db, record_xml):
        synchronizer = current_oai_client.providers["uk"].synchronizers["xoai"]
        oai_sync = OAISync(provider_code="uk")
        synchronizer.oai_sync = oai_sync
        oai_identifier = "oai:dspace.cuni.cz:20.500.11956/2623"

        # vytvoření záznamu
        synchronizer.record_crud(oai_identifier=oai_identifier, xml=record_xml)
        db.session.commit()
        oai_rec = OAIRecord.get_record(oai_identifier)
        assert oai_rec is not None

        # smazání záznamu
        synchronizer.record_crud(oai_rec=oai_rec, deleted=True)
        db.session.commit()
        oai_rec2 = OAIRecord.get_record(oai_identifier)
        assert oai_rec == oai_rec2

        # obnovení záznamu
        synchronizer.record_crud(oai_rec=oai_rec,
                                 timestamp='2050-10-22T08:18:08.567698+00:00',
                                 xml=record_xml)
        db.session.commit()
        oai_rec3 = OAIRecord.get_record(oai_identifier)
        assert oai_rec3 is not None
        record = Record.get_record(oai_rec.id)
        assert record is not None
Пример #22
0
def _get_record_from_workflow(workflow):
    assert len(workflow.objects) == 1
    workflow_object = workflow.objects[0]

    recid = workflow_object.data['control_number']
    pid = PersistentIdentifier.get('recid', recid)

    return Record.get_record(pid.object_uuid)
Пример #23
0
def get_record_from_workflow(workflow):
    assert len(workflow.objects) == 1
    workflow_object = workflow.objects[0]

    recid = workflow_object.data['control_number']
    pid = PersistentIdentifier.get('recid', recid)

    return Record.get_record(pid.object_uuid)
Пример #24
0
 def test_delete_record(self, load_entry_points, app, db, metadata):
     synchronizer = current_oai_client.providers["uk"].synchronizers["xoai"]
     record, id_ = synchronizer.create_record(metadata)
     oai_sync = OAISync(provider_code="uk")
     db.session.add(oai_sync)
     db.session.commit()
     oai_rec = OAIRecord(id=record.id,
                         oai_identifier="oai:example.cz:1",
                         creation_sync_id=oai_sync.id,
                         pid="1",
                         timestamp=datetime.now())
     db.session.add(oai_rec)
     db.session.commit()
     synchronizer.delete_record(oai_rec)
     with pytest.raises(NoResultFound):
         Record.get_record(oai_rec.id)
     deleted_record = Record.get_record(oai_rec.id, with_deleted=True)
Пример #25
0
def acl_deleted_reindex(schemas, acl_id):
    """
    ACL has been deleted so reindex all the documents that contain reference to it.

    :param index: the index of documents
    :param record_acl_id:   if of the ACL instance that has been deleted
    """
    logger.info('Reindexing started for deleted ACL=%s', acl_id)

    acl = ACL.query.filter_by(id=acl_id).one_or_none()

    if acl:  # pragma no cover
        raise AttributeError(
            'ACL with id %s is still in the database, '
            'please remove it before calling current_explicit_acls.reindex_acl_removed'
            % acl_id)

    indexer = RecordIndexer()

    query = {
        "nested": {
            "path": "_invenio_explicit_acls",
            "query": {
                "term": {
                    "_invenio_explicit_acls.id": acl_id
                }
            }
        }
    }
    removed_count = 0
    for schema in schemas:
        current_search_client.indices.refresh(index=schema_to_index(schema)[0])
        current_search_client.indices.flush(index=schema_to_index(schema)[0])
        try:
            index, doc_type = schema_to_index(schema)

            for doc in elasticsearch.helpers.scan(current_search_client,
                                                  query={
                                                      "query": query,
                                                      "_source": False,
                                                  },
                                                  index=index,
                                                  **add_doc_type(doc_type)):
                try:
                    indexer.index(Record.get_record(doc['_id']))
                    removed_count += 1
                except NoResultFound:  # pragma no cover
                    continue
                except:  # pragma no cover
                    logger.exception(
                        'Unexpected exception in record reindexing')
                    continue
        except:  # pragma no cover
            logger.exception('Error removing ACL from schema %s', schema)

    logger.info(
        'Reindexing finished for deleted ACL=%s, acl removed from %s records',
        acl_id, removed_count)
Пример #26
0
def patch_record(recid, patch, validator=None):
    """Patch a record."""
    with db.session.begin_nested():
        record = Record.get_record(recid)
        record = record.patch(patch)
        if validator:
            validator = import_string(validator)
        record.commit(validator=validator)
    return record
Пример #27
0
def check_compliance(obj, *args):
    if 'control_number' not in obj.data:
        raise ValueError(
            "Object should have a 'control_number' key in 'data' dict to be consistent with article upload."
        )

    recid = obj.data['control_number']
    pid = PersistentIdentifier.get('recid', recid)
    record = Record.get_record(pid.object_uuid)

    checks = {}

    # Add temporary data to evaluation
    extra_data = {'extracted_text': __extract_article_text(record)}

    all_checks_accepted = True
    for name, func in COMPLIANCE_TASKS:
        check_accepted, details, debug = func(record, extra_data)
        all_checks_accepted = all_checks_accepted and check_accepted
        checks[name] = {
            'check': check_accepted,
            'details': details,
            'debug': debug
        }

    c = Compliance.get_or_create(pid.object_uuid)
    results = {
        'checks': checks,
        'accepted': all_checks_accepted,
        'data': {
            'doi': get_first_doi(record),
            'publisher': get_abbreviated_publisher(record),
            'journal': get_abbreviated_journal(record),
            'arxiv': get_first_arxiv(record)
        }
    }

    c.add_results(results)
    c.id_record = pid.object_uuid

    db.session.add(c)
    db.session.commit()

    # send notification about failed checks
    need_email = current_app.config.get('COMPLIANCE_SEND_FAILED_EMAILS', True)
    if need_email and not all_checks_accepted and c.has_final_result_changed():
        msg = TemplatedMessage(
            template_html='scoap3_compliance/admin/failed_email.html',
            subject='SCOAP3 - Compliance check',
            sender=current_app.config.get('MAIL_DEFAULT_SENDER'),
            recipients=current_app.config.get('OPERATIONS_EMAILS'),
            ctx={
                'results': results,
                'id': '%s,%s' % (c.id, record.id),
            })
        current_app.extensions['mail'].send(msg)
Пример #28
0
def update_authors_recid(record_id, uuid, profile_recid):
    """Update author profile for a given signature.

    The method receives UUIDs representing record and signature
    respectively together with an author profile recid.
    The new recid will be placed in the signature with the given
    UUID.

    :param record_id:
        A string representing UUID of a given record.

        Example:
            record_id = "a5afb151-8f75-4e91-8dc1-05e7e8e8c0b8"

    :param uuid:
        A string representing UUID of a given signature.

        Example:
            uuid = "c2f432bd-2f52-4c16-ac66-096f168c762f"

    :param profile_recid:
        A string representing author profile recid, that
        updated signature should point to.

        Example:
            profile_recid = "1"
    """
    try:
        record = Record.get_record(record_id)
        update_flag = False

        for author in record['authors']:
            if author['uuid'] == uuid:
                author['recid'] = str(profile_recid)
                update_flag = True

        if update_flag:
            # Disconnect the signal on insert of a new record.
            before_record_index.disconnect(append_updated_record_to_queue)

            # Update the record in the database.
            record.commit()
            db.session.commit()

            # Update the record in Elasticsearch.
            indexer = RecordIndexer()
            indexer.index_by_id(record.id)
    except StaleDataError as exc:
        raise update_authors_recid.retry(exc=exc)
    finally:
        # Reconnect the disconnected signal.
        before_record_index.connect(append_updated_record_to_queue)

    # Report.
    logger.info("Updated signature %s with profile %s",
                uuid, profile_recid)
Пример #29
0
    def _get_csv(self, date_from=None, date_to=None):
        chunk_size = 50
        countries = Gdp.query.order_by(Gdp.name.asc()).all()
        record_ids = ArticlesImpact.query.with_entities(
            ArticlesImpact.control_number).all()
        record_ids = [r[0] for r in record_ids]

        header = [
            'doi', 'recid', 'journal', 'creation_date', 'primary_category',
            'total_authors'
        ]
        header.extend([c.name.strip().replace(',', '') for c in countries])
        si = StringIO.StringIO()
        cw = csv.writer(si, delimiter=";")
        cw.writerow(header)

        for i in range((len(record_ids) // chunk_size) + 1):
            # calculate chunk start and end position
            ixn = i * chunk_size
            current_ids = record_ids[ixn:ixn + chunk_size]

            for record in ArticlesImpact.query.filter(
                    ArticlesImpact.control_number.in_(current_ids)):
                # FIXME during country share refactor, this logic should be moved
                if (date_from is not None and date_from > record.creation_date
                    ) or (date_to is not None
                          and date_to < record.creation_date):
                    continue

                try:
                    r = Record.get_record(
                        PersistentIdentifier.get(
                            'recid', record.control_number).object_uuid)
                    title = get_title(r).lower()

                    if 'addendum' in title or 'corrigendum' in title or 'erratum' in title:
                        continue
                except PIDDoesNotExistError:
                    pass

                total_authors = reduce(lambda x, y: x + y,
                                       record.results.values(), 0)
                country_share = [
                    float(record.results[c.name.strip()]) /
                    total_authors if c.name.strip() in record.results else 0
                    for c in countries
                ]
                primary_category = record.details.get('arxiv_primary_category')
                csv_line = [
                    record.doi, record.control_number, record.journal,
                    record.creation_date, primary_category, total_authors
                ]
                csv_line.extend(country_share)
                cw.writerow(csv_line)

        return si.getvalue()
Пример #30
0
    def get_primary_arxiv_category(self):
        try:
            pid = PersistentIdentifier.get('recid', self.control_number)
            record = Record.get_record(pid.object_uuid)
            return get_arxiv_primary_category(record)
        except PIDDoesNotExistError:
            # records imported from Inspire won't be found
            pass

        return None
Пример #31
0
    def get_primary_arxiv_category(self):
        try:
            pid = PersistentIdentifier.get('recid', self.control_number)
            record = Record.get_record(pid.object_uuid)
            return get_arxiv_primary_category(record)
        except PIDDoesNotExistError:
            # records imported from Inspire won't be found
            pass

        return None
Пример #32
0
def test_delete(testapp, database):
    """Test delete a record."""
    db = database
    # Create a record, revise it and delete it.
    record = Record.create({'title': 'test 1'})
    db.session.commit()
    record['title'] = 'test 2'
    record.commit()
    db.session.commit()
    record.delete()
    db.session.commit()

    # Deleted records a not retrievable by default
    pytest.raises(NoResultFound, Record.get_record, record.id)

    # Deleted records can be retrieved if you explicit request it
    record = Record.get_record(record.id, with_deleted=True)

    # Deleted records are empty
    assert record == {}
    assert record.model.json is None

    # Deleted records *cannot* be modified
    record['title'] = 'deleted'
    assert pytest.raises(MissingModelError, record.commit)

    # Deleted records *can* be reverted
    record = record.revert(-2)
    assert record['title'] == 'test 2'
    db.session.commit()

    # The "undeleted" record can now be retrieve again
    record = Record.get_record(record.id)
    assert record['title'] == 'test 2'

    # Force deleted record cannot be retrieved again
    record.delete(force=True)
    db.session.commit()
    pytest.raises(NoResultFound,
                  Record.get_record,
                  record.id,
                  with_deleted=True)
def get_document_previewer(record_id, filename):
    """Look for a record and return the file."""
    record = Record.get_record(record_id)

    len_documents = len(Document(record, '/files/').record['files'])

    for i_document in range(len_documents):
        document = Document(record, '/files/{0}/uri'.format(i_document))
        document_previewer = DocumentPreviewer(record_id, document)
        if not filename or document_previewer.get_filename() == filename:
            return document_previewer
Пример #34
0
def test_undelete_with_get(testapp, db):
    """Test undelete a record."""
    record = Record.create({'title': 'test'})
    db.session.commit()
    record.delete()
    db.session.commit()
    record = Record.get_record(record.id, with_deleted=True)
    record.undelete()
    record.commit()
    db.session.commit()
    assert record == {}
Пример #35
0
def test_delete(app):
    """Test delete a record."""
    with app.app_context():
        # Create a record, revise it and delete it.
        record = Record.create({'title': 'test 1'})
        db.session.commit()
        record['title'] = 'test 2'
        record.commit()
        db.session.commit()
        record.delete()
        db.session.commit()

        # Deleted records a not retrievable by default
        pytest.raises(NoResultFound, Record.get_record, record.id)

        # Deleted records can be retrieved if you explicit request it
        record = Record.get_record(record.id, with_deleted=True)

        # Deleted records are empty
        assert record == {}
        assert record.model.json is None

        # Deleted records *cannot* be modified
        record['title'] = 'deleted'
        assert pytest.raises(MissingModelError, record.commit)

        # Deleted records *can* be reverted
        record = record.revert(-2)
        assert record['title'] == 'test 2'
        db.session.commit()

        # The "undeleted" record can now be retrieve again
        record = Record.get_record(record.id)
        assert record['title'] == 'test 2'

        # Force deleted record cannot be retrieved again
        record.delete(force=True)
        db.session.commit()
        pytest.raises(
            NoResultFound, Record.get_record, record.id,
            with_deleted=True)
Пример #36
0
def test_taxonomy_term_moved(app, db, taxonomy_tree, test_record):
    taxonomy = current_flask_taxonomies.get_taxonomy("test_taxonomy")
    terms = current_flask_taxonomies.list_taxonomy(taxonomy).all()
    old_record = Record.get_record(id_=test_record.id)
    old_taxonomy = old_record["taxonomy"]
    assert old_taxonomy == [{
        'is_ancestor': True,
        'level': 1,
        'links': {
            'self': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a'
        },
        'test': 'extra_data'
    },
        {
            'is_ancestor': True,
            'level': 2,
            'links': {
                'parent':
                    'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a',
                'self': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a/b'
            },
            'test': 'extra_data'
        },
        {
            'is_ancestor': False,
            'level': 3,
            'links': {
                'parent':
                    'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a/b',
                'self':
                    'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a/b/c'
            },
            'test': 'extra_data'
        }]
    ti = TermIdentification(term=terms[2])
    current_flask_taxonomies.move_term(ti, new_parent=terms[0], remove_after_delete=False)
    db.session.commit()
    new_record = Record.get_record(id_=test_record.id)
    new_taxonomy = new_record["taxonomy"]
    new_terms = current_flask_taxonomies.list_taxonomy(taxonomy).all()
    assert new_terms[-1].parent_id == 1
Пример #37
0
    def test_record_handling(self, load_entry_points, app, db, record_xml):
        synchronizer = current_oai_client.providers["uk"].synchronizers["xoai"]
        oai_sync = OAISync(provider_code="uk")
        db.session.add(oai_sync)
        db.session.commit()
        synchronizer.oai_sync = oai_sync

        synchronizer.record_handling(1, xml=record_xml)
        oai_rec = OAIRecord.get_record(
            oai_identifier="oai:dspace.cuni.cz:20.500.11956/2623")
        record = Record.get_record(id_=oai_rec.id)
        assert record == {'pid': '1', 'title': 'Testovací záznam'}
Пример #38
0
    def test_run_2(self, load_entry_points, app, db):
        patch = mock.patch('sickle.app.Sickle.harvest', mock_harvest)
        patch.start()
        current_oai_client.run(providers_codes=["uk"])
        patch.stop()

        oai_sync = OAISync.query.get(1)
        assert oai_sync.status == "ok"
        assert oai_sync.records_created == 1
        oai_rec = OAIRecord.query.all()[-1]
        assert oai_rec.pid == "1"
        record = Record.get_record(id_=oai_rec.id)
        assert record["title"] == "Testovací záznam"
Пример #39
0
def test_delete_with_sqldatabase_error(app):
    """Test VALID record delete request (GET .../records/<record_id>)."""
    with app.app_context():
        # create the record using the internal API
        pid, record = create_record(test_data)
        db.session.expire(record.model)
        pid_value = pid.pid_value
        pid_type = pid.pid_type
        record_id = record.id

        db.session.commit()
        Record.get_record(record_id)

        def raise_exception():
            raise SQLAlchemyError()

        with app.test_client() as client:
            # start a new SQLAlchemy session so that it will rollback
            # everything
            nested_transaction = db.session().transaction
            orig_rollback = nested_transaction.rollback
            flags = {'rollbacked': False}

            def custom_rollback(*args, **kwargs):
                flags['rollbacked'] = True
                orig_rollback(*args, **kwargs)

            nested_transaction.rollback = custom_rollback

            with patch.object(PersistentIdentifier,
                              'delete',
                              side_effect=raise_exception):
                headers = [('Accept', 'application/json')]
                res = client.delete(url_for('invenio_records_rest.recid_item',
                                            pid_value=pid_value),
                                    headers=headers)
                assert res.status_code == 500
            # check that the transaction is finished
            assert db.session().transaction is not nested_transaction
            # check that the session has rollbacked
            assert flags['rollbacked']

    with app.app_context():
        with app.test_client() as client:
            # check that the record and PID have not been deleted
            Record.get_record(record_id)
            assert not PersistentIdentifier.get(pid_type,
                                                pid_value).is_deleted()
            # try to delete without exception, the transaction should have been
            # rollbacked
            headers = [('Accept', 'application/json')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid_value),
                                headers=headers)
            assert res.status_code == 204
            # check database state
            with pytest.raises(NoResultFound):
                Record.get_record(record_id)
            assert PersistentIdentifier.get(pid_type, pid_value).is_deleted()
Пример #40
0
def test_cli(app):
    """Test CLI."""
    runner = CliRunner()
    script_info = ScriptInfo(create_app=lambda info: app)

    assert 'records_metadata' in db.metadata.tables
    assert 'records_metadata_version' in db.metadata.tables
    assert 'transaction' in db.metadata.tables

    from invenio_records.models import RecordMetadata as RM

    # Test merging a base another file.
    with runner.isolated_filesystem():
        with open('record.json', 'wb') as f:
            f.write(
                json.dumps({
                    "title": "Test"
                }, ensure_ascii=False).encode('utf-8'))

        with open('record.patch', 'wb') as f:
            f.write(
                json.dumps([{
                    "op": "replace",
                    "path": "/title",
                    "value": "Patched Test"
                }],
                           ensure_ascii=False).encode('utf-8'))

        result = runner.invoke(cli.records, [], obj=script_info)
        assert result.exit_code == 0

        with app.app_context():
            assert RM.query.count() == 0

        result = runner.invoke(cli.records, ['create', 'record.json'],
                               obj=script_info)
        assert result.exit_code == 0

        with app.app_context():
            assert RM.query.count() == 1
            recid = RM.query.first().id

        result = runner.invoke(cli.records,
                               ['patch', 'record.patch', '-r', recid],
                               obj=script_info)
        assert result.exit_code == 0

        with app.app_context():
            record = Record.get_record(recid)
            assert record['title'] == 'Patched Test'
            assert record.model.version_id == 2
Пример #41
0
def check_compliance(obj, *args):
    if 'control_number' not in obj.data:
        raise ValueError("Object should have a 'control_number' key in 'data' dict to be consistent with article upload.")

    recid = obj.data['control_number']
    pid = PersistentIdentifier.get('recid', recid)
    record = Record.get_record(pid.object_uuid)

    checks = {}

    # Add temporary data to evalutaion
    extra_data = {'extracted_text': __extract_article_text(record)}

    all_checks_accepted = True
    for name, func in COMPLIANCE_TASKS:
        check_accepted, details, debug = func(record, extra_data)
        all_checks_accepted = all_checks_accepted and check_accepted
        checks[name] = {
            'check': check_accepted,
            'details': details,
            'debug': debug
        }

    c = Compliance.get_or_create(pid.object_uuid)
    results = {
        'checks': checks,
        'accepted': all_checks_accepted,
        'data': {
            'doi': get_first_doi(record),
            'publisher': get_abbreviated_publisher(record),
            'journal': get_abbreviated_journal(record),
            'arxiv': get_first_arxiv(record)
        }
    }

    c.add_results(results)
    c.id_record = pid.object_uuid

    db.session.add(c)
    db.session.commit()

    # send notification about failed checks
    if not all_checks_accepted:
        msg = TemplatedMessage(
            template_html='scoap3_compliance/admin/failed_email.html',
            subject='SCOAP3 - Compliance check',
            sender=current_app.config.get('MAIL_DEFAULT_SENDER'),
            recipients=current_app.config.get('COMPLIANCE_EMAILS'),
            ctx={'results': results}
        )
        current_app.extensions['mail'].send(msg)
def test_delete_with_sqldatabase_error(app):
    """Test VALID record delete request (GET .../records/<record_id>)."""
    with app.app_context():
        # create the record using the internal API
        pid, record = create_record(test_data)
        db.session.expire(record.model)
        pid_value = pid.pid_value
        pid_type = pid.pid_type
        record_id = record.id

        db.session.commit()
        Record.get_record(record_id)

        def raise_exception():
            raise SQLAlchemyError()

        with app.test_client() as client:
            # start a new SQLAlchemy session so that it will rollback
            # everything
            nested_transaction = db.session().transaction
            orig_rollback = nested_transaction.rollback
            flags = {'rollbacked': False}

            def custom_rollback(*args, **kwargs):
                flags['rollbacked'] = True
                orig_rollback(*args, **kwargs)
            nested_transaction.rollback = custom_rollback

            with patch.object(PersistentIdentifier, 'delete',
                              side_effect=raise_exception):
                headers = [('Accept', 'application/json')]
                res = client.delete(url_for('invenio_records_rest.recid_item',
                                            pid_value=pid_value),
                                    headers=headers)
                assert res.status_code == 500
            # check that the transaction is finished
            assert db.session().transaction is not nested_transaction
            # check that the session has rollbacked
            assert flags['rollbacked']

    with app.app_context():
        with app.test_client() as client:
            # check that the record and PID have not been deleted
            Record.get_record(record_id)
            assert not PersistentIdentifier.get(pid_type,
                                                pid_value).is_deleted()
            # try to delete without exception, the transaction should have been
            # rollbacked
            headers = [('Accept', 'application/json')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid_value),
                                headers=headers)
            assert res.status_code == 204
            # check database state
            with pytest.raises(NoResultFound):
                Record.get_record(record_id)
            assert PersistentIdentifier.get(pid_type,
                                            pid_value).is_deleted()
Пример #43
0
def test_delete(app, db):
    """Test delete a record."""
    # Create a record, revise it and delete it.
    record = Record.create({"title": "test 1"})
    db.session.commit()
    record["title"] = "test 2"
    record.commit()
    db.session.commit()
    record.delete()
    db.session.commit()

    # Deleted records a not retrievable by default
    pytest.raises(NoResultFound, Record.get_record, record.id)

    # Deleted records can be retrieved if you explicit request it
    record = Record.get_record(record.id, with_deleted=True)

    # Deleted records are empty
    assert record == {}
    assert record.model.json is None

    # Deleted records *cannot* be modified
    record["title"] = "deleted"
    assert pytest.raises(MissingModelError, record.commit)

    # Deleted records *can* be reverted
    record = record.revert(-2)
    assert record["title"] == "test 2"
    db.session.commit()

    # The "undeleted" record can now be retrieve again
    record = Record.get_record(record.id)
    assert record["title"] == "test 2"

    # Force deleted record cannot be retrieved again
    record.delete(force=True)
    db.session.commit()
    pytest.raises(NoResultFound, Record.get_record, record.id, with_deleted=True)
Пример #44
0
 def record_str(self):
     """Returns a string representation of referenced Record."""
     try:
         rec = Record.get_record(self.record_id)
         if 'title' in rec:
             if '_' in rec['title']:
                 return '%s: %s' % (self.record_id, rec['title']['_'])
             else:
                 return '%s: %s' % (self.record_id, rec['title'])
         else:
             return '%s: %s' % (self.record_id, repr(rec))
     except:
         pass
     return 'No record for ' + str(self)
Пример #45
0
def test_record_abuse_report(app, test_records, test_users,
                             login_user):
    """Test abuse reports send email."""
    data = dict(
        message='my message',
        name='my name',
        affiliation='my affiliation',
        email='*****@*****.**',
        address='my address',
        city='my city',
        country='my country',
        zipcode='my zipcode',
        phone='my phone',
        noresearch=True,
        abusecontent=False,
        copyright=False,
        illegalcontent=False,
    )
    with app.app_context():
        record = Record.get_record(test_records[0].record_id)
        with app.test_client() as client:
            user = test_users['normal']
            login_user(user, client)

            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]

            with app.extensions['mail'].record_messages() as outbox:
                request_res = client.post(
                    url_for('b2share_records_rest.b2rec_abuse',
                            pid_value=test_records[0].pid),
                    data=json.dumps(data),
                    headers=headers)

                assert request_res.status_code == 200
                assert len(outbox) == 1
                email = outbox[0]
                assert email.recipients == [app.config.get('SUPPORT_EMAIL')]
                assert "Message: {}".format(data['message']) in email.body
                assert "Reason: No research data" in email.body
                assert "Link: {}".format(
                    url_for('b2share_records_rest.b2rec_item',
                            pid_value=test_records[0].pid),
                ) in email.body
                request_data = json.loads(request_res.get_data(as_text=True))
                assert request_data == {
                    'message':'The record is reported.'
                }
Пример #46
0
    def proc(article_impact):
        for country, val in article_impact.results.items():
            if country not in data['countries']:
                data['countries'][country] = 0

            data['countries'][country] += val

        try:
            record = Record.get_record(PersistentIdentifier.get('recid', article_impact.control_number).object_uuid)
            author_count = len(record['authors'])
        except PIDDoesNotExistError:
            author_count = len(article_impact.details['authors'])

        sum_values = sum(article_impact.results.values())
        if sum_values != author_count:
            data['not_one'].add((article_impact.control_number, sum_values, author_count))
def test_cli(app):
    """Test CLI."""
    runner = CliRunner()
    script_info = ScriptInfo(create_app=lambda info: app)

    assert 'records_metadata' in db.metadata.tables
    assert 'records_metadata_version' in db.metadata.tables
    assert 'transaction' in db.metadata.tables

    from invenio_records.models import RecordMetadata as RM

    # Test merging a base another file.
    with runner.isolated_filesystem():
        with open('record.json', 'wb') as f:
            f.write(json.dumps(
                {"title": "Test"}, ensure_ascii=False
            ).encode('utf-8'))

        with open('record.patch', 'wb') as f:
            f.write(json.dumps([{
                "op": "replace", "path": "/title", "value": "Patched Test"
            }], ensure_ascii=False).encode('utf-8'))

        result = runner.invoke(cli.records, [], obj=script_info)
        assert result.exit_code == 0

        with app.app_context():
            assert RM.query.count() == 0

        result = runner.invoke(cli.records, ['create', 'record.json'],
                               obj=script_info)
        assert result.exit_code == 0

        with app.app_context():
            assert RM.query.count() == 1
            recid = RM.query.first().id

        result = runner.invoke(cli.records,
                               ['patch', 'record.patch', '-r', recid],
                               obj=script_info)
        assert result.exit_code == 0

        with app.app_context():
            record = Record.get_record(recid)
            assert record['title'] == 'Patched Test'
            assert record.model.version_id == 2
Пример #48
0
def record_upsert(json):
    """Insert or update a record."""
    control_number = json.get('control_number', json.get('recid'))
    if control_number:
        control_number = int(control_number)
        pid_type = InspireRecordIdProvider.schema_to_pid_type(json['$schema'])
        try:
            pid = PersistentIdentifier.get(pid_type, control_number)
            record = Record.get_record(pid.object_uuid)
            record.update(json)
            record.commit()
        except PIDDoesNotExistError:
            record = Record.create(json, id_=None)
            # Create persistent identifier.
            inspire_recid_minter(str(record.id), json)

        return record
Пример #49
0
def test_record_put_is_disabled(app, test_records, test_users,
                                 login_user):
    """Test invalid modification of record draft with HTTP PUT."""
    with app.app_context():
        record = Record.get_record(test_records[0].record_id)
        with app.test_client() as client:
            user = test_users['admin']
            login_user(user, client)

            headers = [('Content-Type', 'application/json'),
                       ('Accept', 'application/json')]
            draft_put_res = client.put(
                url_for('b2share_records_rest.b2rec_item',
                        pid_value=test_records[0].pid),
                data='{}',
                headers=headers)
            assert draft_put_res.status_code == 405
Пример #50
0
    def _get_csv(self, date_from=None, date_to=None):
        chunk_size = 50
        countries = Gdp.query.order_by(Gdp.name.asc()).all()
        record_ids = ArticlesImpact.query.with_entities(ArticlesImpact.control_number).all()
        record_ids = [r[0] for r in record_ids]

        header = ['doi', 'recid', 'journal', 'creation_date', 'primary_category', 'total_authors']
        header.extend([c.name.strip().replace(',', '') for c in countries])
        si = StringIO.StringIO()
        cw = csv.writer(si, delimiter=";")
        cw.writerow(header)

        for i in range((len(record_ids) // chunk_size) + 1):
            # calculate chunk start and end position
            ixn = i * chunk_size
            current_ids = record_ids[ixn:ixn + chunk_size]

            for record in ArticlesImpact.query.filter(ArticlesImpact.control_number.in_(current_ids)):
                # FIXME during country share refactor, this logic should be moved
                if (date_from is not None and date_from > record.creation_date) or (
                        date_to is not None and date_to < record.creation_date):
                    continue

                try:
                    r = Record.get_record(PersistentIdentifier.get('recid', record.control_number).object_uuid)
                    title = get_title(r).lower()

                    if 'addendum' in title or 'corrigendum' in title or 'erratum' in title:
                        continue
                except PIDDoesNotExistError:
                    pass

                total_authors = reduce(lambda x, y: x + y,
                                       record.results.values(), 0)
                country_share = [float(record.results[c.name.strip()]) / total_authors
                                 if c.name.strip() in record.results else 0
                                 for c in countries]
                primary_category = record.details.get('arxiv_primary_category')
                csv_line = [record.doi, record.control_number, record.journal, record.creation_date, primary_category,
                            total_authors]
                csv_line.extend(country_share)
                cw.writerow(csv_line)

        return si.getvalue()
Пример #51
0
def update_datacite_metadata(doi, object_uuid, job_id):
    """Update DataCite metadata of a single PersistentIdentifier.

    :param doi: Value of doi PID, with pid_type='doi'. It could be a normal
    DOI or a concept DOI.
    :type doi: str
    :param object_uuid: Record Metadata UUID.
    :type object_uuid: str
    :param job_id: id of the job to which this task belongs.
    :type job_id: str
    """
    task_details = current_cache.get('update_datacite:task_details')

    if task_details is None or job_id != task_details['job_id']:
        return

    record = Record.get_record(object_uuid)

    dcp = DataCiteProvider.get(doi)
    if dcp.pid.status != PIDStatus.REGISTERED:
        return

    doc = datacite_v41.serialize(dcp.pid, record)

    for validator in xsd41():
        validator.assertValid(etree.XML(doc.encode('utf8')))

    url = None
    if doi == record.get('doi'):
        url = current_app.config['ZENODO_RECORDS_UI_LINKS_FORMAT'].format(
            recid=str(record['recid']))
    elif doi == record.get('conceptdoi'):
        url = current_app.config['ZENODO_RECORDS_UI_LINKS_FORMAT'].format(
            recid=str(record['conceptrecid']))

    result = dcp.update(url, doc)
    if result is True:
        dcp.pid.updated = datetime.utcnow()
        db.session.commit()
Пример #52
0
def test_record_add_unknown_fields(app, test_records):
    """Test adding unknown fields in a record. It should fail."""
    for path in [
        # all the following paths point to "object" fields in
        # in the root JSON Schema
        '/new_field',
        '/community_specific/new_field',
        '/creators/0/new_field',
        '/titles/0/new_field',
        '/contributors/0/new_field',
        '/resource_types/0/new_field',
        '/alternate_identifiers/0/new_field',
        '/descriptions/0/new_field',
        '/license/new_field',
    ]:
        with app.app_context():
            record = Record.get_record(test_records[0].record_id)
            record = record.patch([
                {'op': 'add', 'path': path, 'value': 'any value'}
            ])
            with pytest.raises(ValidationError):
                record.commit()
Пример #53
0
    def proc(article_impact):
        try:
            if 'arxiv_primary_category' in article_impact.details:
                return

            pid = PersistentIdentifier.get('recid', article_impact.control_number)
            record = Record.get_record(pid.object_uuid)

            if not record:
                return

            if 'arxiv_eprints' in record:
                info('%d: eprints found' % article_impact.control_number)
                arxiv = (record['arxiv_eprints'][0]['value'].split(':')[1]).split('v')[0]
                cat = get_arxiv_categories(arxiv)[0]
                info('category: %s' % cat)
                if cat:
                    article_impact.details['arxiv_primary_category'] = cat
                    flag_modified(article_impact, 'details')

            elif 'report_numbers' in record:
                info('%d: report_numbers found' % article_impact.control_number)
                cat = get_arxiv_primary_category(record)
                info('category: %s' % cat)
                if cat:
                    article_impact.details['arxiv_primary_category'] = cat
                    flag_modified(article_impact, 'details')

            else:
                error('%d: no arxiv' % article_impact.control_number)

        except PIDDoesNotExistError:
            # records imported from Inspire won't be found
            pass
        except AttributeError as e:
            error('%d: %s' % (article_impact.control_number, e))
Пример #54
0
def test_record_access_request(app, test_records, test_users,
                               login_user):
    """Test access requests send email."""
    data = dict(
        message='my message',
        name='my name',
        affiliation='my affiliation',
        email='*****@*****.**',
        address='my address',
        city='my city',
        country='my country',
        zipcode='my zipcode',
        phone='my phone',
    )
    with app.app_context():
        record = Record.get_record(test_records[0].record_id)
        with app.test_client() as client:
            user = test_users['normal']
            login_user(user, client)

            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]

            # test with contact_email
            with app.extensions['mail'].record_messages() as outbox:
                request_res = client.post(
                    url_for('b2share_records_rest.b2rec_accessrequests',
                            pid_value=test_records[0].pid),
                    data=json.dumps(data),
                    headers=headers)

                assert request_res.status_code == 200
                assert len(outbox) == 1
                email = outbox[0]
                assert email.recipients == [record['contact_email']]
                assert "Message: {}".format(data['message']) in email.body
                assert "Link: {}".format(
                    url_for('b2share_records_rest.b2rec_item',
                            pid_value=test_records[0].pid),
                ) in email.body
                request_data = json.loads(request_res.get_data(as_text=True))
                assert request_data == {
                    'message': 'An email was sent to the record owner.'
                }

            # test with owners
            del record['contact_email']
            record.commit()
            with app.extensions['mail'].record_messages() as outbox:
                request_res = client.post(
                    url_for('b2share_records_rest.b2rec_accessrequests',
                            pid_value=test_records[0].pid),
                    data=json.dumps(data),
                    headers=headers)

                assert request_res.status_code == 200
                assert len(outbox) == 1
                email = outbox[0]
                assert email.recipients == [
                    test_users['deposits_creator'].email
                ]
                assert "Message: {}".format(data['message']) in email.body
                assert "Link: {}".format(
                    url_for('b2share_records_rest.b2rec_item',
                            pid_value=test_records[0].pid),
                ) in email.body
                request_data = json.loads(request_res.get_data(as_text=True))
                assert request_data == {
                    'message': 'An email was sent to the record owner.'
                }
def test_db():
    """Test database backend."""
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get(
        'SQLALCHEMY_DATABASE_URI', 'sqlite:///test.db'
    )
    FlaskCLI(app)
    InvenioDB(app)
    InvenioRecords(app)

    with app.app_context():
        db.drop_all()
        db.create_all()
        assert 'records_metadata' in db.metadata.tables
        assert 'records_metadata_version' in db.metadata.tables
        assert 'transaction' in db.metadata.tables

    schema = {
        'type': 'object',
        'properties': {
            'title': {'type': 'string'},
            'field': {'type': 'boolean'},
            'hello': {'type': 'array'},
        },
        'required': ['title'],
    }
    data = {'title': 'Test', '$schema': schema}
    from invenio_records.models import RecordMetadata as RM

    # Create a record
    with app.app_context():
        assert RM.query.count() == 0

        record_uuid = Record.create(data).id
        db.session.commit()

        assert RM.query.count() == 1
        db.session.commit()

    # Retrieve created record
    with app.app_context():
        record = Record.get_record(record_uuid)
        assert record.dumps() == data
        with pytest.raises(NoResultFound):
            Record.get_record(uuid.uuid4())
        record['field'] = True
        record = record.patch([
            {'op': 'add', 'path': '/hello', 'value': ['world']}
        ])
        assert record['hello'] == ['world']
        record.commit()
        db.session.commit()

    with app.app_context():
        record2 = Record.get_record(record_uuid)
        assert record2.model.version_id == 2
        assert record2['field']
        assert record2['hello'] == ['world']
        db.session.commit()

    # Cannot commit record without model (i.e. Record.create_record)
    with app.app_context():
        record3 = Record({'title': 'Not possible'})
        with pytest.raises(MissingModelError):
            record3.commit()

    # Check invalid schema values
    with app.app_context():
        data = {
            '$schema': 'http://json-schema.org/geo#',
            'latitude': 42,
            'longitude': 42,
        }
        record_with_schema = Record.create(data).commit()
        db.session.commit()

        record_with_schema['latitude'] = 'invalid'
        with pytest.raises(ValidationError):
            record_with_schema.commit()

    # Allow types overriding on schema validation
    with app.app_context():
        data = {
            'title': 'Test',
            'hello': tuple(['foo', 'bar']),
            '$schema': schema
        }
        app.config['RECORDS_VALIDATION_TYPES'] = {}
        with pytest.raises(ValidationError):
            Record.create(data).commit()

        app.config['RECORDS_VALIDATION_TYPES'] = {'array': (list, tuple)}
        record_uuid = Record.create(data).commit()
        db.session.commit()

    with app.app_context():
        db.drop_all()
def test_cli(app):
    """Test CLI."""
    runner = CliRunner()
    script_info = ScriptInfo(create_app=lambda info: app)

    assert 'records_metadata' in db.metadata.tables
    assert 'records_metadata_version' in db.metadata.tables
    assert 'transaction' in db.metadata.tables

    from invenio_records.models import RecordMetadata as RM

    # Test merging a base another file.
    with runner.isolated_filesystem():
        with open('record.json', 'wb') as f:
            f.write(json.dumps(
                {'title': 'Test'}, ensure_ascii=False
            ).encode('utf-8'))

        with open('records.json', 'wb') as f:
            f.write(json.dumps(
                [{'title': 'Test1'}, {'title': 'Test2'}], ensure_ascii=False
            ).encode('utf-8'))

        with open('record.patch', 'wb') as f:
            f.write(json.dumps([{
                'op': 'replace', 'path': '/title', 'value': 'Patched Test'
            }], ensure_ascii=False).encode('utf-8'))

        result = runner.invoke(cli.records, [], obj=script_info)
        assert result.exit_code == 0

        with app.app_context():
            assert RM.query.count() == 0

        result = runner.invoke(cli.records, ['create', 'record.json'],
                               obj=script_info)
        assert result.exit_code == 0
        recid = result.output.split('\n')[0]

        with app.app_context():
            assert RM.query.count() == 1
            assert recid == str(RM.query.first().id)

        result = runner.invoke(cli.records,
                               ['patch', 'record.patch', '-i', recid],
                               obj=script_info)
        assert result.exit_code == 0

        with app.app_context():
            record = Record.get_record(recid)
            assert record['title'] == 'Patched Test'
            assert record.model.version_id == 2

        # Test generated UUIDs
        recid1 = uuid.uuid4()
        recid2 = uuid.uuid4()
        assert recid1 != recid2

        # More ids than records.
        result = runner.invoke(
            cli.records,
            ['create', 'record.json', '-i', recid1, '--id', recid2],
            obj=script_info
        )
        assert result.exit_code == -1

        result = runner.invoke(
            cli.records,
            ['create', 'record.json', '-i', recid],
            obj=script_info
        )
        assert result.exit_code == 2

        result = runner.invoke(
            cli.records,
            ['create', 'record.json', '-i', recid, '--force'],
            obj=script_info
        )
        assert result.exit_code == 0

        with app.app_context():
            record = Record.get_record(recid)
            assert record.model.version_id == 3

        result = runner.invoke(
            cli.records,
            ['create', 'records.json', '-i', recid1],
            obj=script_info
        )
        assert result.exit_code == -1

        result = runner.invoke(
            cli.records,
            ['create', 'records.json', '-i', recid1, '-i', recid2],
            obj=script_info
        )
        assert result.exit_code == 0
        with app.app_context():
            assert 3 == RM.query.count()

        # Check metadata after force insert.
        result = runner.invoke(
            cli.records,
            ['create', 'record.json', '-i', recid1, '--force'],
            obj=script_info
        )
        assert result.exit_code == 0
        with app.app_context():
            record = Record.get_record(recid1)
            assert 'Test' == record['title']
            assert 'Test1' == record.revisions[0]['title']

        # More modifications of record 1.
        result = runner.invoke(
            cli.records,
            ['create', 'records.json', '-i', recid1, '--id', recid2,
             '--force'],
            obj=script_info
        )
        assert result.exit_code == 0
        with app.app_context():
            record = Record.get_record(recid1)
            assert 'Test1' == record['title']
            assert 'Test' == record.revisions[1]['title']
            assert 'Test1' == record.revisions[0]['title']
def test_db():
    """Test database backend."""
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get(
        'SQLALCHEMY_DATABASE_URI', 'sqlite:///test.db'
    )
    FlaskCLI(app)
    InvenioDB(app)
    InvenioRecords(app)

    with app.app_context():
        db.create_all()
        assert 'records_metadata' in db.metadata.tables
        assert 'records_metadata_version' in db.metadata.tables
        assert 'transaction' in db.metadata.tables

    schema = {
        'type': 'object',
        'properties': {
            'title': {'type': 'string'},
            'field': {'type': 'boolean'},
            'hello': {'type': 'array'},
        },
        'required': ['title'],
    }
    data = {'title': 'Test', '$schema': schema}
    from invenio_records.models import RecordMetadata as RM

    # Create a record
    with app.app_context():
        assert RM.query.count() == 0

        record_uuid = Record.create(data).id
        db.session.commit()

        assert RM.query.count() == 1
        db.session.commit()

    # Retrieve created record
    with app.app_context():
        record = Record.get_record(record_uuid)
        assert record.dumps() == data
        with pytest.raises(NoResultFound):
            Record.get_record(uuid.uuid4())
        record['field'] = True
        record = record.patch([
            {'op': 'add', 'path': '/hello', 'value': ['world']}
        ])
        assert record['hello'] == ['world']
        record.commit()
        db.session.commit()

    with app.app_context():
        record2 = Record.get_record(record_uuid)
        assert record2.model.version_id == 2
        assert record2['field']
        assert record2['hello'] == ['world']
        db.session.commit()

    # Cannot commit record without model (i.e. Record.create_record)
    with app.app_context():
        record3 = Record({'title': 'Not possible'})
        with pytest.raises(MissingModelError):
            record3.commit()

    with app.app_context():
        db.drop_all()
Пример #58
0
def test_revisions(app, db):
    """Test revisions."""
    # Create a record and make modifications to it.
    record = Record.create({"title": "test 1"})
    rec_uuid = record.id
    db.session.commit()
    record["title"] = "test 2"
    record.commit()
    db.session.commit()
    record["title"] = "test 3"
    record.commit()
    db.session.commit()

    # Get the record
    record = Record.get_record(rec_uuid)
    assert record["title"] == "test 3"
    assert record.revision_id == 2

    # Retrieve specific revisions
    rev1 = record.revisions[0]
    assert rev1["title"] == "test 1"
    assert rev1.revision_id == 0

    rev2 = record.revisions[1]
    assert rev2["title"] == "test 2"
    assert rev2.revision_id == 1

    # Latest revision is identical to record.
    rev_latest = record.revisions[-1]
    assert dict(rev_latest) == dict(record)

    # Revert to a specific revision
    record = record.revert(rev1.revision_id)
    assert record["title"] == "test 1"
    assert record.created == rev1.created
    assert record.updated != rev1.updated
    assert record.revision_id == 3
    db.session.commit()

    # Get the record again and check it
    record = Record.get_record(rec_uuid)
    assert record["title"] == "test 1"
    assert record.revision_id == 3

    # Make a change and ensure revision id is changed as well.
    record["title"] = "modification"
    record.commit()
    db.session.commit()
    assert record.revision_id == 4

    # Iterate over revisions
    assert len(record.revisions) == 5
    revs = list(record.revisions)
    assert revs[0]["title"] == "test 1"
    assert revs[1]["title"] == "test 2"
    assert revs[2]["title"] == "test 3"
    assert revs[3]["title"] == "test 1"
    assert revs[4]["title"] == "modification"

    assert 2 in record.revisions
    assert 5 not in record.revisions