def test_add_remove_corner_cases(app, db, communities, disable_request_email): """Test corner cases for community adding and removal.""" (comm1, comm2, comm3) = communities communities_key = app.config["COMMUNITIES_RECORD_KEY"] # Create a record and create an inclusion request, # meanwhile adding a record manually outside the workflow rec1 = Record.create({'title': 'Foobar'}) InclusionRequest.create(community=comm1, record=rec1) rec1[communities_key] = [ 'comm1', ] # Add community manually assert InclusionRequest.query.count() == 1 # Accepting record which was added manually outside the normal workflow comm1.accept_record(rec1) # Should still work assert rec1[communities_key] == [ 'comm1', ] assert InclusionRequest.query.count() == 0 assert comm1.oaiset.has_record(rec1) # Removing record which was removed manually outside the normal workflow rec1[communities_key] = [] # Remove community manually comm1.remove_record(rec1) # Should still work assert communities_key in rec1 assert len(rec1[communities_key]) == 0 assert not comm1.oaiset.has_record(rec1)
def test_email_notification(app, db, communities, user): """Test mail notification sending for community request.""" # Mock the send method of the Flask-Mail extension with app.extensions['mail'].record_messages() as outbox: (comm1, comm2, comm3) = communities # Create a record and accept it into the community by creating an # InclusionRequest and then calling the accept action rec1 = Record.create({'title': 'Foobar', 'description': 'Baz bar.'}) InclusionRequest.create(community=comm1, record=rec1, user=user) assert len(outbox) == 1
def test_email_notification(app, db, communities, user): """Test mail notification sending for community request.""" # Mock the send method of the Flask-Mail extension with app.extensions['mail'].record_messages() as outbox: (comm1, comm2, comm3) = communities # Create a record and accept it into the community by creating an # InclusionRequest and then calling the accept action rec1 = Record.create({ 'title': 'Foobar', 'description': 'Baz bar.'}) InclusionRequest.create(community=comm1, record=rec1, user=user) assert len(outbox) == 1
def _remove_obsolete_irs(comms, record): """Remove obsolete inclusion requests. :param comms: Community IDs as declared in deposit. Used to remove obsolete inclusion requests. :type comms: list of str :param record: Record corresponding to this deposit. :type record: `invenio_records.api.Record` """ InclusionRequest.get_by_record(record.id).filter( InclusionRequest.id_community.notin_(comms)).delete( synchronize_session='fetch')
def migrate_record(record_uuid, logger=None): """Migrate a record.""" try: # Migrate record record = Record.get_record(record_uuid) if '$schema' in record: if logger: logger.info("Record already migrated.") return record = transform_record(record) provisional_communities = record.pop('provisional_communities', None) record.commit() # Create provisional communities. if provisional_communities: for c_id in provisional_communities: try: c = Community.get(c_id) if c: InclusionRequest.create(c, record, notify=False) else: if logger: logger.warning( "Community {0} does not exists " "(record {1}).".format( c_id, str(record.id))) except InclusionRequestExistsError: if logger: logger.warning("Inclusion request exists.") # Register DOI doi = record.get('doi') if doi: is_internal = doi.startswith('10.5281') PersistentIdentifier.create( pid_type='doi', pid_value=doi, pid_provider='datacite' if is_internal else None, object_type='rec', object_uuid=record_uuid, status=( PIDStatus.REGISTERED if is_internal else PIDStatus.RESERVED), ) db.session.commit() except NoResultFound: if logger: logger.info("Deleted record - no migration required.") except Exception: db.session.rollback() pid = PersistentIdentifier.get_by_object('recid', 'rec', record_uuid) pid.status = PIDStatus.RESERVED db.session.commit() raise
def _create_inclusion_requests(comms, record): """Create inclusion requests for communities. :param comms: Community IDs for which the inclusion requests might should be created (if they don't exist already). :type comms: list of str :param record: Record corresponding to this deposit. :type record: `invenio_records.api.Record` """ for comm_id in comms: comm = Community.get(comm_id) if not InclusionRequest.get(comm_id, record.id): InclusionRequest.create(comm, record)
def test_community_delete_task(app, db, communities): """Test the community deletion task.""" (comm1, comm2, comm3) = communities communities_key = app.config["COMMUNITIES_RECORD_KEY"] rec1 = Record.create({'title': 'Foobar'}) InclusionRequest.create(community=comm1, record=rec1, notify=False) assert InclusionRequest.get(comm1.id, rec1.id) comm1.accept_record(rec1) assert 'comm1' in rec1[communities_key] comm1.delete() assert comm1.is_deleted
def migrate_record(record_uuid, logger=None): """Migrate a record.""" try: # Migrate record record = Record.get_record(record_uuid) if '$schema' in record: if logger: logger.info("Record already migrated.") return record = transform_record(record) provisional_communities = record.pop('provisional_communities', None) record.commit() # Create provisional communities. if provisional_communities: for c_id in provisional_communities: try: c = Community.get(c_id) if c: InclusionRequest.create(c, record, notify=False) else: if logger: logger.warning("Community {0} does not exists " "(record {1}).".format( c_id, str(record.id))) except InclusionRequestExistsError: if logger: logger.warning("Inclusion request exists.") # Register DOI doi = record.get('doi') if doi: is_internal = doi.startswith('10.5281') PersistentIdentifier.create( pid_type='doi', pid_value=doi, pid_provider='datacite' if is_internal else None, object_type='rec', object_uuid=record_uuid, status=(PIDStatus.REGISTERED if is_internal else PIDStatus.RESERVED), ) db.session.commit() except NoResultFound: if logger: logger.info("Deleted record - no migration required.") except Exception: db.session.rollback() pid = PersistentIdentifier.get_by_object('recid', 'rec', record_uuid) pid.status = PIDStatus.RESERVED db.session.commit() raise
def _create_inclusion_requests(comms, record): """Create inclusion requests for communities. :param comms: Community IDs for which the inclusion requests might should be created (if they don't exist already). :type comms: list of str :param record: Record corresponding to this deposit. :type record: `invenio_records.api.Record` """ for comm_id in comms: comm_api = ZenodoCommunity(comm_id) # Check if InclusionRequest exists for any version already pending_irs = comm_api.get_comm_irs(record) if pending_irs.count() == 0 and not comm_api.has_record(record): comm = Community.get(comm_id) InclusionRequest.create(comm, record)
def _prepare_edit(self, record): """Prepare deposit for editing. Extend the deposit's communities metadata by the pending inclusion requests. """ data = super(ZenodoDeposit, self)._prepare_edit(record) data.setdefault('communities', []).extend( [c.id_community for c in InclusionRequest.get_by_record(record.id)]) data['communities'] = sorted(list(set(data['communities']))) # Remove the OpenAIRE subtype if the record is no longer pending, # nor in the relevant community oa_type = data['resource_type'].get('openaire_subtype') if oa_type and not is_valid_openaire_type(data['resource_type'], data['communities']): del data['resource_type']['openaire_subtype'] if not data['communities']: del data['communities'] # If non-Zenodo DOI unlock the bucket to allow file-editing if not is_doi_locally_managed(data['doi']): self.files.bucket.locked = False return data
def test_email_formatting(app, db, communities, user): """Test formatting of the email message with the default template.""" with app.extensions['mail'].record_messages() as outbox: (comm1, comm2, comm3) = communities rec1 = Record.create({ 'title': 'Foobar and Bazbar', 'description': 'On Foobar, Bazbar and <b>more</b>.' }) # Request InclusionRequest.create(community=comm1, record=rec1, user=user) # Check emails being sent assert len(outbox) == 1 sent_msg = outbox[0] assert sent_msg.recipients == [user.email] assert comm1.title in sent_msg.body
def test_model_init(app): """Test basic model initialization and actions.""" with app.app_context(): # Init the User and the Community user1 = create_test_user() comm1 = Community(id='comm1', id_user=user1.id) db.session.add(comm1) db.session.commit() communities_key = app.config["COMMUNITIES_RECORD_KEY"] # Create a record and accept it into the community by creating an # InclusionRequest and then calling the accept action rec1 = Record.create({'title': 'Foobar'}) InclusionRequest.create(community=comm1, record=rec1) assert InclusionRequest.query.count() == 1 comm1.accept_record(rec1) assert 'comm1' in rec1[communities_key] assert InclusionRequest.query.count() == 0 # Likewise, reject a record from the community rec2 = Record.create({'title': 'Bazbar'}) InclusionRequest.create(community=comm1, record=rec2) assert InclusionRequest.query.count() == 1 comm1.reject_record(rec2) assert communities_key not in rec2 # dict key should not be created assert InclusionRequest.query.count() == 0 # Add record to another community comm2 = Community(id='comm2', id_user=user1.id) db.session.add(comm2) db.session.commit() InclusionRequest.create(community=comm2, record=rec1) comm2.accept_record(rec1) assert communities_key in rec1 assert len(rec1[communities_key]) == 2 assert comm1.id in rec1[communities_key] assert comm2.id in rec1[communities_key] # Accept/reject a record to/from a community without inclusion request rec3 = Record.create({'title': 'Spam'}) pytest.raises(InclusionRequestMissingError, comm1.accept_record, rec3) pytest.raises(InclusionRequestMissingError, comm1.reject_record, rec3) # Create two inclusion requests comm3 = Community(id='comm3', id_user=user1.id) db.session.add(comm3) db.session.commit() InclusionRequest.create(community=comm3, record=rec1) pytest.raises(InclusionRequestExistsError, InclusionRequest.create, community=comm3, record=rec1) # Try to accept a record to a community twice (should raise) # (comm1 is already in rec1) pytest.raises(InclusionRequestObsoleteError, InclusionRequest.create, community=comm1, record=rec1)
def test_model_init(app, db, communities): """Test basic model initialization and actions.""" (comm1, comm2, comm3) = communities communities_key = app.config["COMMUNITIES_RECORD_KEY"] # Create a record and accept it into the community by creating an # InclusionRequest and then calling the accept action rec1 = Record.create({'title': 'Foobar'}) InclusionRequest.create(community=comm1, record=rec1) assert InclusionRequest.query.count() == 1 comm1.accept_record(rec1) assert 'comm1' in rec1[communities_key] assert InclusionRequest.query.count() == 0 # Likewise, reject a record from the community rec2 = Record.create({'title': 'Bazbar'}) InclusionRequest.create(community=comm1, record=rec2) assert InclusionRequest.query.count() == 1 comm1.reject_record(rec2) assert communities_key not in rec2 # dict key should not be created assert InclusionRequest.query.count() == 0 # Add record to another community InclusionRequest.create(community=comm2, record=rec1) comm2.accept_record(rec1) assert communities_key in rec1 assert len(rec1[communities_key]) == 2 assert comm1.id in rec1[communities_key] assert comm2.id in rec1[communities_key] # Accept/reject a record to/from a community without inclusion request rec3 = Record.create({'title': 'Spam'}) pytest.raises(InclusionRequestMissingError, comm1.accept_record, rec3) pytest.raises(InclusionRequestMissingError, comm1.reject_record, rec3) # Create two inclusion requests InclusionRequest.create(community=comm3, record=rec1) db.session.commit() db.session.flush() pytest.raises(InclusionRequestExistsError, InclusionRequest.create, community=comm3, record=rec1) # Try to accept a record to a community twice (should raise) # (comm1 is already in rec1) pytest.raises(InclusionRequestObsoleteError, InclusionRequest.create, community=comm1, record=rec1)
def _prepare_edit(self, record): """Prepare deposit for editing. Extend the deposit's communities metadata by the pending inclusion requests. """ data = super(ZenodoDeposit, self)._prepare_edit(record) data.setdefault('communities', []).extend( [c.id_community for c in InclusionRequest.get_by_record(record.id)]) data['communities'] = sorted(list(set(data['communities']))) if not data['communities']: del data['communities'] return data
def _prepare_edit(self, record): """Prepare deposit for editing. Extend the deposit's communities metadata by the pending inclusion requests. """ data = super(ZenodoDeposit, self)._prepare_edit(record) data.setdefault('communities', []).extend([ c.id_community for c in InclusionRequest.get_by_record(record.id) ]) data['communities'] = sorted(list(set(data['communities']))) if not data['communities']: del data['communities'] return data
def test_add_remove_corner_cases(app, db, communities, disable_request_email): """Test corner cases for community adding and removal.""" (comm1, comm2, comm3) = communities communities_key = app.config["COMMUNITIES_RECORD_KEY"] # Create a record and create an inclusion request, # meanwhile adding a record manually outside the workflow rec1 = Record.create({'title': 'Foobar'}) InclusionRequest.create(community=comm1, record=rec1) rec1[communities_key] = ['comm1', ] # Add community manually assert InclusionRequest.query.count() == 1 # Accepting record which was added manually outside the normal workflow comm1.accept_record(rec1) # Should still work assert rec1[communities_key] == ['comm1', ] assert InclusionRequest.query.count() == 0 assert comm1.oaiset.has_record(rec1) # Removing record which was removed manually outside the normal workflow rec1[communities_key] = [] # Remove community manually comm1.remove_record(rec1) # Should still work assert communities_key in rec1 assert len(rec1[communities_key]) == 0 assert not comm1.oaiset.has_record(rec1)
def _prepare_edit(self, record): """Prepare deposit for editing. Extend the deposit's communities metadata by the pending inclusion requests. """ data = super(ZenodoDeposit, self)._prepare_edit(record) data.setdefault('communities', []).extend( [c.id_community for c in InclusionRequest.get_by_record(record.id)]) data['communities'] = sorted(list(set(data['communities']))) # Remove the OpenAIRE subtype if the record is no longer pending, # nor in the relevant community oa_type = data['resource_type'].get('openaire_subtype') if oa_type and not is_valid_openaire_type(data['resource_type'], data['communities']): del data['resource_type']['openaire_subtype'] if not data['communities']: del data['communities'] return data
def suggest(): """Index page with uploader and list of existing depositions. :param community_id: ID of the community to curate. """ community = None record = None url = request.referrer if "url" in request.values and request.values["url"]: url = request.values["url"] if not "community" in request.values: flash(u"Error, no {} given".format( current_app.config["COMMUNITIES_NAME"]), "danger") return redirect(url) community_id = request.values["community"] community = Community.get(community_id) if not community: flash(u"Error, unknown {} {}".format( current_app.config["COMMUNITIES_NAME"], community_id), "danger") return redirect(url) if not _get_permission("communities-read", community).can() \ and not DynamicPermission(ActionNeed('admin-access')).can(): flash(u"Error, you don't have permissions on the {} {}".format( current_app.config["COMMUNITIES_NAME"], community_id), "danger") return redirect(url) if not "recpid" in request.values: flash(u"Error, no record given", "danger") return redirect(url) recid = request.values["recpid"] resolver = Resolver( pid_type='recid', object_type='rec', getter=Record.get_record) try: pid, record = resolver.resolve(recid) except Exception: flash(u"Error, unkown record {}".format(recid), "danger") return redirect(url) # if the user has the curate permission on this community, # we automatically add the record if _get_permission("communities-curate", community).can(): try: community.add_record(record) except: # the record is already in the community flash(u"The record already exists in the {} {}.".format( current_app.config["COMMUNITIES_NAME"], community.title), "warning") else: record.commit() flash(u"The record has been added to the {} {}.".format( current_app.config["COMMUNITIES_NAME"], community.title)) # otherwise we only suggest it and it will appear in the curate list else: try: InclusionRequest.create(community=community, record=record, user=current_user) except InclusionRequestObsoleteError: # the record is already in the community flash(u"The record already exists in the {} {}.".format( current_app.config["COMMUNITIES_NAME"], community.title), "warning") except InclusionRequestExistsError: flash(u"The record has already been suggested " u"to the {} {}.".format( current_app.config["COMMUNITIES_NAME"], community.title), "warning") else: flash(u"The record has been suggested " u"to the {} {}.".format( current_app.config["COMMUNITIES_NAME"], community.title)) db.session.commit() RecordIndexer().index_by_id(record.id) return redirect(url)