def delete(cls, id, account, dry_run=False): # as long as authentication (in the layer above) has been successful, and the account exists, then # we are good to proceed if account is None: raise Api401Error() applicationService = DOAJ.applicationService() authService = DOAJ.authorisationService() if dry_run: application, _ = applicationService.application(id) if application is not None: try: authService.can_edit_application(account, application) except AuthoriseException as e: if e.reason == e.WRONG_STATUS: raise Api403Error() raise Api404Error() else: raise Api404Error() else: try: applicationService.delete_application(id, account) except AuthoriseException as e: if e.reason == e.WRONG_STATUS: raise Api403Error() raise Api404Error() except NoSuchObjectException as e: raise Api404Error()
def update_request_readonly(application_id): # DOAJ BLL for this request applicationService = DOAJ.applicationService() authService = DOAJ.authorisationService() application, _ = applicationService.application(application_id) try: authService.can_view_application(current_user._get_current_object(), application) except AuthoriseException as e: abort(404) fc = formcontext.ApplicationFormFactory.get_form_context( role="update_request_readonly", source=application) return fc.render_template(no_sidebar=True)
def setUp(self): super(TestBLLArticleBatchCreateArticle, self).setUp() self.svc = DOAJ.articleService() self._is_legitimate_owner = self.svc.is_legitimate_owner self._get_duplicate = self.svc.get_duplicate self._issn_ownership_status = self.svc.issn_ownership_status self._get_journal = Article.get_journal
def metadata(): # if this is a get request, give the blank form - there is no edit feature if request.method == "GET": form = ArticleForm() return render_template('publisher/metadata.html', form=form) # if this is a post request, a form button has been hit and we need to do # a bunch of work elif request.method == "POST": form = ArticleForm(request.form) # first we need to do any server-side form modifications which # the user might request by pressing the add/remove authors buttons more_authors = request.values.get("more_authors") remove_author = None for v in request.values.keys(): if v.startswith("remove_authors"): remove_author = v.split("-")[1] # if the user wants more authors, add an extra entry if more_authors: form.authors.append_entry() return render_template('publisher/metadata.html', form=form) # if the user wants to remove an author, do the various back-flips required if remove_author is not None: keep = [] while len(form.authors.entries) > 0: entry = form.authors.pop_entry() if entry.short_name == "authors-" + remove_author: break else: keep.append(entry) while len(keep) > 0: form.authors.append_entry(keep.pop().data) return render_template('publisher/metadata.html', form=form) # if we get to here, then this is the full submission, and we need to # validate and return enough_authors = _validate_authors(form) if form.validate(): # if the form validates, then we have to do our own bit of validation, # which is to check that there is at least one author supplied if not enough_authors: return render_template('publisher/metadata.html', form=form, author_error=True) else: xwalk = ArticleFormXWalk() art = xwalk.crosswalk_form(form) articleService = DOAJ.articleService() articleService.create_article( art, current_user._get_current_object()) flash("Article created/updated", "success") form = ArticleForm() return render_template('publisher/metadata.html', form=form) else: return render_template('publisher/metadata.html', form=form, author_error=not enough_authors)
def test_02_get_application(self, name, application, application_id, account, lock_application, raises=None): if lock_application: lock.lock(constants.LOCK_APPLICATION, application.id, "someoneelse", blocking=True) svc = DOAJ.applicationService() if application is not None: application.save(blocking=True) if raises is not None: with self.assertRaises(raises): if account is None: retrieved, _ = svc.application(application_id) else: retrieved, jlock = svc.application(application_id, lock_application=True, lock_account=account) else: if account is None: retrieved, _ = svc.application(application_id) if retrieved is not None: assert retrieved.data == application.data else: assert retrieved is None else: retrieved, jlock = svc.application(application_id, lock_application=True, lock_account=account) if retrieved is not None: assert retrieved.data == application.data else: assert retrieved is None time.sleep(2) assert lock.has_lock(constants.LOCK_APPLICATION, application_id, account.id)
def test_01_get_journal(self, name, journal, journal_id, account, lock_journal, raises=None): if lock_journal: lock.lock("journal", journal.id, "someoneelse", blocking=True) svc = DOAJ.journalService() if journal is not None: journal.save(blocking=True) if raises is not None: with self.assertRaises(raises): if account is None: retrieved, _ = svc.journal(journal_id) else: retrieved, jlock = svc.journal(journal_id, lock_journal=True, lock_account=account) else: if account is None: retrieved, _ = svc.journal(journal_id) if retrieved is not None: assert retrieved.data == journal.data else: assert retrieved is None else: retrieved, jlock = svc.journal(journal_id, lock_journal=True, lock_account=account) if retrieved is not None: assert retrieved.data == journal.data else: assert retrieved is None time.sleep(2) assert lock.has_lock("journal", journal_id, account.id)
def setUp(self): super(TestBLLPrepareUpdatePublisher, self).setUp() self.svc = DOAJ.articleService() self.is_id_updated = self.svc._doi_or_fulltext_updated self.has_permission = self.svc.has_permissions self.merge = Article.merge acc_source = AccountFixtureFactory.make_publisher_source() self.publisher = Account(**acc_source)
def test_01_create_update_request(self, name, journal, account, raises=None, expected=None): svc = DOAJ.authorisationService() if raises is not None: with self.assertRaises(raises): svc.can_create_update_request(account, journal) elif expected is not None: assert svc.can_create_update_request(account, journal) is expected else: assert False, "Specify either raises or expected"
def setUp(self): super(TestBLLArticleCreateArticle, self).setUp() self.svc = DOAJ.articleService() self.is_legitimate_owner = self.svc.is_legitimate_owner self.ownership = self.svc.issn_ownership_status self.duplicate = self.svc.get_duplicate self.permission = self.svc.has_permissions self.prepare_update_admin = self.svc._prepare_update_admin self.prepare_update_publisher = self.svc._prepare_update_publisher
def test_02_edit_update_request(self, name, application, account, raises=None, expected=None): svc = DOAJ.authorisationService() if raises is not None: with self.assertRaises(raises): svc.can_edit_application(account, application) elif expected is not None: assert svc.can_edit_application(account, application) is expected else: assert False, "Specify either raises or expected"
def test_01_journal_2_application(self, name, journal, account, raises=None, comparator=None): svc = DOAJ.journalService() if raises is not None: with self.assertRaises(raises): svc.journal_2_application(journal, account) elif comparator is not None: application = svc.journal_2_application(journal, account) comparator(journal, application) else: assert False, "Specify either raises or comparator"
def test_03_view_application(self, name, account_type, role, owner, application_type, raises=None, returns=None, auth_reason=None): # set up the objects application = None if application_type == "exists": application = Suggestion( **ApplicationFixtureFactory.make_application_source()) account = None if account_type == "exists": if role == "none": account = Account( **AccountFixtureFactory.make_publisher_source()) account.remove_role("publisher") elif role == "publisher": account = Account( **AccountFixtureFactory.make_publisher_source()) elif role == "admin": account = Account( **AccountFixtureFactory.make_managing_editor_source()) if owner == "yes": application.set_owner(account.id) svc = DOAJ.authorisationService() if raises is not None and raises != "": exception = None with self.assertRaises(EXCEPTIONS[raises]): try: svc.can_view_application(account, application) except Exception as e: exception = e raise e if raises == "AuthoriseException": if auth_reason == "not_owner": assert exception.reason == exception.NOT_OWNER elif auth_reason == "wrong_role": assert exception.reason == exception.WRONG_ROLE elif returns is not None: expected = returns == "true" assert svc.can_view_application(account, application) is expected else: assert False, "Specify either raises or returns"
def setUp(self): super(TestBLLJournalCSV, self).setUp() self.svc = DOAJ.journalService() self.store_tmp_dir = app.config["STORE_TMP_DIR"] app.config["STORE_TMP_DIR"] = os.path.join("test_store", "tmp") self.store_local_dir = app.config["STORE_LOCAL_DIR"] app.config["STORE_LOCAL_DIR"] = os.path.join("test_store", "main") self.localStore = store.StoreLocal(None) self.tmpStore = store.TempStore() self.container_id = app.config.get("STORE_CACHE_CONTAINER") self.store_tmp_impl = app.config["STORE_TMP_IMPL"] self.store_impl = app.config["STORE_IMPL"] self.cache = models.cache.Cache models.cache.Cache = ModelCacheMockFactory.in_memory() models.Cache = models.cache.Cache
def create(cls, articles, account): # We run through the articles once, validating in dry-run mode # and deduplicating as we go. Then we .save() everything once # we know all incoming articles are valid. # as long as authentication (in the layer above) has been successful, and the account exists, then # we are good to proceed if account is None: raise Api401Error() # convert the data into a suitable article models articles = [ArticlesCrudApi.prep_article(data) for data in articles] articleService = DOAJ.articleService() try: result = articleService.batch_create_articles(articles, account, add_journal_info=True) return [a.id for a in articles] except exceptions.IngestException as e: raise Api400Error(e.message)
def create(cls, articles, account): # We run through the articles once, validating in dry-run mode # and deduplicating as we go. Then we .save() everything once # we know all incoming articles are valid. # as long as authentication (in the layer above) has been successful, and the account exists, then # we are good to proceed if account is None: raise Api401Error() # convert the data into a suitable article models articles = [ArticlesCrudApi.prep_article(data) for data in articles] articleService = DOAJ.articleService() try: result = articleService.batch_create_articles(articles, account) return [a.id for a in articles] except exceptions.IngestException as e: raise Api400Error(e.message)
def test_03_view_application(self, name, account_type, role, owner, application_type, raises=None, returns=None, auth_reason=None): # set up the objects application = None if application_type == "exists": application = Suggestion(**ApplicationFixtureFactory.make_application_source()) account = None if account_type == "exists": if role == "none": account = Account(**AccountFixtureFactory.make_publisher_source()) account.remove_role("publisher") elif role == "publisher": account = Account(**AccountFixtureFactory.make_publisher_source()) elif role == "admin": account = Account(**AccountFixtureFactory.make_managing_editor_source()) if owner == "yes": application.set_owner(account.id) svc = DOAJ.authorisationService() if raises is not None and raises != "": exception = None with self.assertRaises(EXCEPTIONS[raises]): try: svc.can_view_application(account, application) except Exception as e: exception = e raise e if raises == "AuthoriseException": if auth_reason == "not_owner": assert exception.reason == exception.NOT_OWNER elif auth_reason == "wrong_role": assert exception.reason == exception.WRONG_ROLE elif returns is not None: expected = returns == "true" assert svc.can_view_application(account, application) is expected else: assert False, "Specify either raises or returns"
def test_01_discover_duplicates(self, name, kwargs): article_arg = kwargs.get("article") owner_arg = kwargs.get("owner") article_doi_arg = kwargs.get("article_doi") doi_duplicate_arg = kwargs.get("doi_duplicate") article_fulltext_arg = kwargs.get("article_fulltext") fulltext_duplicate_arg = kwargs.get("fulltext_duplicate") articles_by_doi_arg = kwargs.get("articles_by_doi") articles_by_fulltext_arg = kwargs.get("articles_by_fulltext") raises_arg = kwargs.get("raises") raises = EXCEPTIONS.get(raises_arg) ############################################### ## set up owner = None if owner_arg != "none": owner = Account(**AccountFixtureFactory.make_publisher_source()) owner_id = None if owner is not None: owner_id = owner.id # create a journal for the owner if owner_arg not in ["none"]: source = JournalFixtureFactory.make_journal_source(in_doaj=True) journal = Journal(**source) journal.set_owner(owner.id) journal.bibjson().remove_identifiers() journal.bibjson().add_identifier("eissn", "1234-5678") journal.bibjson().add_identifier("pissn", "9876-5432") journal.save(blocking=True) # determine what we need to load into the index article_ids = [] aids_block = [] if owner_arg not in ["none", "no_articles"]: for i, ident in enumerate(IDENTS): the_doi = ident["doi"] if doi_duplicate_arg == "padded": the_doi = " " + the_doi + " " elif doi_duplicate_arg == "prefixed": the_doi = "https://dx.doi.org/" + the_doi the_fulltext = ident["fulltext"] if article_fulltext_arg != "invalid": if fulltext_duplicate_arg == "padded": the_fulltext = " http:" + the_fulltext elif fulltext_duplicate_arg == "http": the_fulltext = "http:" + the_fulltext elif fulltext_duplicate_arg == "https": the_fulltext = "https:" + the_fulltext else: the_fulltext = "http:" + the_fulltext source = ArticleFixtureFactory.make_article_source(eissn="1234-5678", pissn="9876-5432", doi=the_doi, fulltext=the_fulltext) article = Article(**source) article.set_id() article.save() article_ids.append(article.id) aids_block.append((article.id, article.last_updated)) # generate our incoming article article = None doi = None fulltext = None if article_arg == "yes": eissn = "1234=5678" # one matching pissn = "6789-1234" # the other not - issn matches are not relevant to this test if article_doi_arg in ["yes", "padded"]: doi = "10.1234/abc/11" if doi_duplicate_arg in ["yes", "padded"]: doi = IDENTS[0]["doi"] if article_doi_arg == "padded": doi = " doi:" + doi + " " elif article_doi_arg in ["invalid"]: doi = IDENTS[-1]["doi"] if article_fulltext_arg in ["yes", "padded", "https"]: fulltext = "//example.com/11" if fulltext_duplicate_arg in ["yes", "padded", "https"]: fulltext = IDENTS[0]["fulltext"] if fulltext_duplicate_arg == "padded": fulltext = " http:" + fulltext + " " elif fulltext_duplicate_arg == "https": fulltext = "https:" + fulltext else: fulltext = "http:" + fulltext elif article_fulltext_arg == "invalid": fulltext = IDENTS[-1]["fulltext"] source = ArticleFixtureFactory.make_article_source(eissn=eissn, pissn=pissn, doi=doi, fulltext=fulltext) article = Article(**source) # we need to do this if doi or fulltext are none, because the factory will set a default if we don't # provide them if doi is None: article.bibjson().remove_identifiers("doi") if fulltext is None: article.bibjson().remove_urls("fulltext") article.set_id() Article.blockall(aids_block) ########################################################### # Execution svc = DOAJ.articleService() if raises is not None: with self.assertRaises(raises): svc.discover_duplicates(article, owner_id) else: possible_articles = svc.discover_duplicates(article, owner_id) if articles_by_doi_arg == "yes": assert "doi" in possible_articles assert len(possible_articles["doi"]) == 1 # if this is the "invalid" doi, then we expect it to match the final article, otherwise match the first if article_doi_arg == "invalid": assert possible_articles["doi"][0].id == article_ids[-1] else: assert possible_articles["doi"][0].id == article_ids[0] else: if possible_articles is not None: assert "doi" not in possible_articles if articles_by_fulltext_arg == "yes": assert "fulltext" in possible_articles assert len(possible_articles["fulltext"]) == 1 # if this is the "invalid" fulltext url, then we expect it to match the final article, otherwise match the first if article_fulltext_arg == "invalid": assert possible_articles["fulltext"][0].id == article_ids[-1] else: assert possible_articles["fulltext"][0].id == article_ids[0] else: if possible_articles is not None: assert "fulltext" not in possible_articles
def setUp(self): super(TestBLLPrepareUpdatePublisher, self).setUp() self.svc = DOAJ.articleService() self.is_id_updated = self.svc._doi_or_fulltext_updated self.merge = Article.merge self.pull = Article.pull
def test_01_is_legitimate_owner(self, name, kwargs): article_arg = kwargs.get("article") owner_arg = kwargs.get("owner") article_eissn_arg = kwargs.get("article_eissn") article_pissn_arg = kwargs.get("article_pissn") seen_eissn_arg = kwargs.get("seen_eissn") seen_pissn_arg = kwargs.get("seen_pissn") journal_owner_arg = kwargs.get("journal_owner") raises_arg = kwargs.get("raises") legit_arg = kwargs.get("legit") raises = EXCEPTIONS.get(raises_arg) ############################################### ## set up owner = None if owner_arg != "none": owner = Account(**AccountFixtureFactory.make_publisher_source()) owner_id = None if owner is not None: owner_id = owner.id # generate our incoming article article = None eissn = None pissn = None if article_arg == "exists": source = ArticleFixtureFactory.make_article_source() article = Article(**source) article.set_id() article.bibjson().remove_identifiers("pissn") if article_pissn_arg == "yes": pissn = "1234-5678" article.bibjson().add_identifier("pissn", pissn) article.bibjson().remove_identifiers("eissn") if article_eissn_arg == "yes": eissn = "9876-5432" article.bibjson().add_identifier("eissn", eissn) # assemble the issns that will appear to be in the index. One that is irrelevant, and just # serves to be "noise" in the database, and the other that matches the spec required by # the test issns = [("1111-1111", "2222-2222")] if eissn is not None and pissn is not None and seen_eissn_arg == "yes" and seen_pissn_arg == "yes": issns.append((eissn, pissn)) if eissn is not None and seen_eissn_arg == "yes": issns.append((eissn, None)) if pissn is not None and seen_pissn_arg == "yes": issns.append((None, pissn)) owners = [] if journal_owner_arg == "none": owners = [None] elif journal_owner_arg == "correct" and owner_id is not None: owners = [owner_id] elif journal_owner_arg == "incorrect": owners = ["randomowner"] elif journal_owner_arg == "mix" and owner_id is not None: owners.append(owner_id) owners.append("randomowner") owners.append(None) mock = ModelJournalMockFactory.find_by_issn(issns, owners) Journal.find_by_issn = mock ########################################################### # Execution svc = DOAJ.articleService() if raises is not None: with self.assertRaises(raises): svc.is_legitimate_owner(article, owner_id) else: legit = svc.is_legitimate_owner(article, owner_id) if legit_arg == "no": assert legit is False elif legit_arg == "yes": assert legit is True
def test_02_application_2_journal(self, name, application_type, manual_update_arg, app_key_properties, current_journal, raises): # set up for the test ######################################### cj = None has_seal = bool(randint(0, 1)) application = None if application_type == "present": application = Suggestion( **ApplicationFixtureFactory.make_application_source()) application.set_id(application.makeid()) application.remove_contacts() application.remove_editor_group() application.remove_editor() application.remove_owner() application.remove_current_journal() application.remove_notes() if app_key_properties == "yes": application.add_contact("Application", "*****@*****.**") application.set_editor_group("appeditorgroup") application.set_editor("appeditor") application.set_owner("appowner") application.set_seal(has_seal) application.add_note("Application Note") if current_journal == "present": journal = Journal( **JournalFixtureFactory.make_journal_source()) journal.remove_contacts() journal.add_contact("Journal", "*****@*****.**") journal.set_editor_group("journaleditorgroup") journal.set_editor("journaleditor") journal.set_owner("journalowner") journal.remove_current_application() journal.remove_notes() journal.add_note("Journal Note") journal.save(blocking=True) application.set_current_journal(journal.id) cj = journal elif current_journal == "missing": application.set_current_journal("123456789987654321") manual_update = None if manual_update_arg == "true": manual_update = True elif manual_update_arg == "false": manual_update = False # execute the test ######################################## svc = DOAJ.applicationService() if raises is not None and raises != "": with self.assertRaises(EXCEPTIONS[raises]): svc.application_2_journal(application, manual_update) else: journal = svc.application_2_journal(application, manual_update) # check the result ###################################### assert journal is not None assert isinstance(journal, Journal) assert journal.is_in_doaj() is True jbj = journal.bibjson().data del jbj["active"] assert jbj == application.bibjson().data if current_journal == "present": assert len(journal.related_applications) == 3 else: assert len(journal.related_applications) == 1 related = journal.related_application_record(application.id) assert related is not None if manual_update_arg == "true": assert journal.last_manual_update is not None and journal.last_manual_update != "1970-01-01T00:00:00Z" if app_key_properties == "yes": contacts = journal.contacts() assert len(contacts) == 1 assert contacts[0].get("name") == "Application" assert contacts[0].get("email") == "*****@*****.**" assert journal.editor_group == "appeditorgroup" assert journal.editor == "appeditor" assert journal.owner == "appowner" assert journal.has_seal() == has_seal if current_journal == "present": assert len(journal.notes) == 2 else: assert len(journal.notes) == 1 elif app_key_properties == "no": if current_journal == "present": contacts = journal.contacts() assert len(contacts) == 1 assert contacts[0].get("name") == "Journal" assert contacts[0].get("email") == "*****@*****.**" assert journal.editor_group == "journaleditorgroup" assert journal.editor == "journaleditor" assert journal.owner == "journalowner" assert journal.has_seal() == has_seal assert len(journal.notes) == 2 elif current_journal == "none" or current_journal == "missing": contacts = journal.contacts() assert len(contacts) == 0 assert journal.editor_group is None assert journal.editor is None assert journal.owner is None assert journal.has_seal() == has_seal assert len(journal.notes) == 1 if current_journal == "present": assert cj.id == journal.id assert cj.created_date == journal.created_date
def test_01_reject_application(self, name, application, application_status, account, prov, current_journal, note, save, raises=None): ####################################### ## set up if save == "fail": Suggestion.save = mock_save_fail ap = None journal = None if application == "exists": ap = Suggestion( **ApplicationFixtureFactory.make_application_source()) ap.set_application_status(application_status) ap.set_id(ap.makeid()) ap.remove_notes() if current_journal == "yes": journal = Journal(**JournalFixtureFactory.make_journal_source( in_doaj=True)) journal.set_id(journal.makeid()) journal.set_current_application(ap.id) journal.save(blocking=True) ap.set_current_journal(journal.id) else: ap.remove_current_journal() acc = None if account == "publisher": acc = Account(**AccountFixtureFactory.make_publisher_source()) elif account == "admin": acc = Account( **AccountFixtureFactory.make_managing_editor_source()) provenance = None if prov != "none": provenance = prov == "true" thenote = None if note == "yes": thenote = "abcdefg" ######################################## ## execute svc = DOAJ.applicationService() if raises is not None and raises != "": with self.assertRaises(EXCEPTIONS[raises]): svc.reject_application(ap, acc, provenance, note=thenote) else: svc.reject_application(ap, acc, provenance, note=thenote) time.sleep(1) ####################################### ## Check ap2 = Suggestion.pull(ap.id) assert ap2 is not None assert ap2.application_status == constants.APPLICATION_STATUS_REJECTED assert ap2.current_journal is None # check the updated and manually updated date are essentially the same (they can theoretically differ # by a small amount just based on when they are set) updated_spread = abs( (ap2.last_updated_timestamp - ap2.last_manual_update_timestamp).total_seconds()) assert updated_spread <= 1.0 if current_journal == "yes" and journal is not None: j2 = Journal.pull(journal.id) assert j2 is not None assert j2.current_application is None assert ap2.related_journal == j2.id if prov == "true": pr = Provenance.get_latest_by_resource_id(ap.id) assert pr is not None if note == "yes": assert len(ap2.notes) == 1 assert ap2.notes[0].get("note") == "abcdefg" elif note == "no": assert len(ap2.notes) == 0
def update_request(journal_id): # DOAJ BLL for this request journalService = DOAJ.journalService() applicationService = DOAJ.applicationService() # if this is a delete request, deal with it first and separately from the below logic if request.method == "DELETE": journal, _ = journalService.journal(journal_id) application_id = journal.current_application if application_id is not None: applicationService.delete_application( application_id, current_user._get_current_object()) else: abort(404) return "" # load the application either directly or by crosswalking the journal object application = None jlock = None alock = None try: application, jlock, alock = applicationService.update_request_for_journal( journal_id, account=current_user._get_current_object()) except AuthoriseException as e: if e.reason == AuthoriseException.WRONG_STATUS: journal, _ = journalService.journal(journal_id) return render_template( "publisher/application_already_submitted.html", journal=journal) else: abort(404) except lock.Locked as e: journal, _ = journalService.journal(journal_id) return render_template("publisher/locked.html", journal=journal, lock=e.lock) # if we didn't find an application or journal, 404 the user if application is None: if jlock is not None: jlock.delete() if alock is not None: alock.delete() abort(404) # if we have a live application and cancel was hit, then cancel the operation and redirect # first determine if this is a cancel request on the form cancelled = request.values.get("cancel") if cancelled is not None: if jlock is not None: jlock.delete() if alock is not None: alock.delete() return redirect(url_for("publisher.updates_in_progress")) # if we are requesting the page with a GET, we just want to show the form if request.method == "GET": fc = formcontext.ApplicationFormFactory.get_form_context( role="publisher", source=application) return fc.render_template(edit_suggestion_page=True) # if we are requesting the page with a POST, we need to accept the data and handle it elif request.method == "POST": fc = formcontext.ApplicationFormFactory.get_form_context( role="publisher", form_data=request.form, source=application) if fc.validate(): try: fc.finalise() Messages.flash(Messages.APPLICATION_UPDATE_SUBMITTED_FLASH) for a in fc.alert: Messages.flash_with_url(a, "success") return redirect(url_for("publisher.updates_in_progress")) except formcontext.FormContextException as e: Messages.flash(e.message) return redirect( url_for("publisher.update_request", journal_id=journal_id, _anchor='cannot_edit')) finally: if jlock is not None: jlock.delete() if alock is not None: alock.delete() else: return fc.render_template(edit_suggestion_page=True)
def test_01_delete_application(self, name, application_type, account_type, current_journal, related_journal, raises): ############################################### ## set up # create the test application (if needed), and the associated current_journal and related_journal in suitable states application = None cj = None rj = None if application_type == "found" or application_type == "locked": application = Suggestion( **ApplicationFixtureFactory.make_application_source()) if current_journal == "none": application.remove_current_journal() elif current_journal == "not_found": application.set_current_journal("123456789987654321") elif current_journal == "found": cj = Journal(**JournalFixtureFactory.make_journal_source()) cj.set_id(cj.makeid()) cj.save(blocking=True) application.set_current_journal(cj.id) elif current_journal == "locked": cj = Journal(**JournalFixtureFactory.make_journal_source()) cj.set_id(cj.makeid()) cj.save(blocking=True) application.set_current_journal(cj.id) lock.lock(constants.LOCK_JOURNAL, cj.id, "otheruser") if related_journal == "none": application.remove_related_journal() elif related_journal == "not_found": application.set_related_journal("123456789987654321") elif related_journal == "found": rj = Journal(**JournalFixtureFactory.make_journal_source()) rj.set_id(rj.makeid()) rj.save(blocking=True) application.set_related_journal(rj.id) elif related_journal == "locked": rj = Journal(**JournalFixtureFactory.make_journal_source()) rj.set_id(rj.makeid()) rj.save(blocking=True) application.set_related_journal(rj.id) lock.lock(constants.LOCK_JOURNAL, rj.id, "otheruser") acc = None if account_type != "none": acc = Account(**AccountFixtureFactory.make_publisher_source()) if account_type == "not_permitted": acc.remove_role("publisher") if application_type == "locked": thelock = lock.lock(constants.LOCK_APPLICATION, application.id, "otheruser") # we can't explicitly block on the lock, but we can halt until we confirm it is saved thelock.blockall([(thelock.id, thelock.last_updated)]) application_id = None if application is not None: if acc is not None: application.set_owner(acc.id) application.save(blocking=True) application_id = application.id elif application_type == "not_found": application_id = "sdjfasofwefkwflkajdfasjd" ########################################################### # Execution svc = DOAJ.applicationService() if raises != "": with self.assertRaises(EXCEPTIONS[raises]): svc.delete_application(application_id, acc) time.sleep(1) check_locks(application, cj, rj, acc) else: svc.delete_application(application_id, acc) # we need to sleep, so the index catches up time.sleep(1) # check that no locks remain set for this user check_locks(application, cj, rj, acc) # check that the application actually is gone if application is not None: assert Suggestion.pull(application.id) is None # check that the current journal no longer has a reference to the application if cj is not None: cj = Journal.pull(cj.id) assert cj.current_application is None # check that the related journal has a record that the application was deleted if rj is not None: rj = Journal.pull(rj.id) record = rj.related_application_record(application.id) assert "status" in record assert record["status"] == "deleted"
def _process(self, file_upload): job = self.background_job upload_dir = app.config.get("UPLOAD_DIR") path = os.path.join(upload_dir, file_upload.local_filename) if not os.path.exists(path): job.add_audit_message( u"File not found at path {} . Retrying job later.".format( path)) count = self.get_param(job.params, "attempts") retry_limit = app.config.get("HUEY_TASKS", {}).get("ingest_articles", {}).get("retries", 0) self.set_param(job.params, "attempts", count + 1) if retry_limit <= count: job.add_audit_message( u"File still not found at path {} . Giving up.".format( path)) job.fail() raise RetryException() job.add_audit_message(u"Importing from {x}".format(x=path)) articleService = DOAJ.articleService() account = models.Account.pull(file_upload.owner) xwalk_name = app.config.get("ARTICLE_CROSSWALKS", {}).get(file_upload.schema) xwalk = plugin.load_class(xwalk_name)() ingest_exception = False result = {} try: with open(path) as handle: articles = xwalk.crosswalk_file( handle, add_journal_info=False ) # don't import the journal info, as we haven't validated ownership of the ISSNs in the article yet for article in articles: article.set_upload_id(file_upload.id) result = articleService.batch_create_articles( articles, account, add_journal_info=True) except IngestException as e: job.add_audit_message( u"IngestException: {msg}. Inner message: {inner}. Stack: {x}". format(msg=e.message, inner=e.inner_message, x=e.trace())) file_upload.failed(e.message, e.inner_message) result = e.result try: file_failed(path) ingest_exception = True except: job.add_audit_message( u"Error cleaning up file which caused IngestException: {x}" .format(x=traceback.format_exc())) except (DuplicateArticleException, ArticleNotAcceptable) as e: job.add_audit_message( u"One or more articles did not contain either a DOI or a Fulltext URL" ) file_upload.failed( u"One or more articles did not contain either a DOI or a Fulltext URL" ) try: file_failed(path) except: job.add_audit_message( u"Error cleaning up file which caused Exception: {x}". format(x=traceback.format_exc())) return except Exception as e: job.add_audit_message( u"Unanticipated error: {x}".format(x=traceback.format_exc())) file_upload.failed("Unanticipated error when importing articles") try: file_failed(path) except: job.add_audit_message( u"Error cleaning up file which caused Exception: {x}". format(x=traceback.format_exc())) return success = result.get("success", 0) fail = result.get("fail", 0) update = result.get("update", 0) new = result.get("new", 0) shared = result.get("shared", []) unowned = result.get("unowned", []) unmatched = result.get("unmatched", []) if success == 0 and fail > 0 and not ingest_exception: file_upload.failed("All articles in file failed to import") job.add_audit_message("All articles in file failed to import") if success > 0 and fail == 0: file_upload.processed(success, update, new) if success > 0 and fail > 0: file_upload.partial(success, fail, update, new) job.add_audit_message( "Some articles in file failed to import correctly, so no articles imported" ) file_upload.set_failure_reasons(list(shared), list(unowned), list(unmatched)) job.add_audit_message("Shared ISSNs: " + ", ".join(list(shared))) job.add_audit_message("Unowned ISSNs: " + ", ".join(list(unowned))) job.add_audit_message("Unmatched ISSNs: " + ", ".join(list(unmatched))) if not ingest_exception: try: os.remove(path) # just remove the file, no need to keep it except Exception as e: job.add_audit_message( u"Error while deleting file {x}: {y}".format(x=path, y=e.message))
def create(cls, data, account, dry_run=False): # as long as authentication (in the layer above) has been successful, and the account exists, then # we are good to proceed if account is None: raise Api401Error() # first thing to do is a structural validation, but instantiating the data object try: ia = IncomingApplication(data) except dataobj.DataStructureException as e: raise Api400Error(e.message) # if that works, convert it to a Suggestion object ap = ia.to_application_model() # now augment the suggestion object with all the additional information it requires # # suggester name and email from the user account ap.set_suggester(account.name, account.email) # they are not allowed to set "subject" ap.bibjson().remove_subjects() # if this is an update request on an existing journal if ap.current_journal is not None: # DOAJ BLL for this request applicationService = DOAJ.applicationService() # load the update_request application either directly or by crosswalking the journal object vanilla_ap = None jlock = None alock = None try: vanilla_ap, jlock, alock = applicationService.update_request_for_journal(ap.current_journal, account=account) except AuthoriseException as e: if e.reason == AuthoriseException.WRONG_STATUS: raise Api403Error("The application is no longer in a state in which it can be edited via the API") else: raise Api404Error() except lock.Locked as e: raise Api409Error("The application you are requesting an update for is locked for editing by another user") # if we didn't find an application or journal, 404 the user if vanilla_ap is None: if jlock is not None: jlock.delete() if alock is not None: alock.delete() raise Api404Error() # convert the incoming application into the web form form = MultiDict(xwalk.SuggestionFormXWalk.obj2form(ap)) fc = formcontext.ApplicationFormFactory.get_form_context(role="publisher", form_data=form, source=vanilla_ap) if fc.validate(): try: save_target = not dry_run fc.finalise(save_target=save_target, email_alert=False) return fc.target except formcontext.FormContextException as e: raise Api400Error(e.message) finally: if jlock is not None: jlock.delete() if alock is not None: alock.delete() else: if jlock is not None: jlock.delete() if alock is not None: alock.delete() raise Api400Error(cls._validation_message(fc)) # otherwise, this is a brand-new application else: # convert the incoming application into the web form form = MultiDict(xwalk.SuggestionFormXWalk.obj2form(ap)) # create a template that will hold all the values we want to persist across the form submission template = models.Suggestion() template.set_owner(account.id) fc = formcontext.ApplicationFormFactory.get_form_context(form_data=form, source=template) if fc.validate(): try: save_target = not dry_run fc.finalise(save_target=save_target, email_alert=False) return fc.target except formcontext.FormContextException as e: raise Api400Error(e.message) else: raise Api400Error(cls._validation_message(fc))
def test_01_reject_application(self, name, application, application_status, account, prov, current_journal, note, save, raises=None): ####################################### ## set up if save == "fail": Suggestion.save = mock_save_fail ap = None journal = None if application == "exists": ap = Suggestion(**ApplicationFixtureFactory.make_application_source()) ap.set_application_status(application_status) ap.set_id(ap.makeid()) ap.remove_notes() if current_journal == "yes": journal = Journal(**JournalFixtureFactory.make_journal_source(in_doaj=True)) journal.set_id(journal.makeid()) journal.set_current_application(ap.id) journal.save(blocking=True) ap.set_current_journal(journal.id) else: ap.remove_current_journal() acc = None if account == "publisher": acc = Account(**AccountFixtureFactory.make_publisher_source()) elif account == "admin": acc = Account(**AccountFixtureFactory.make_managing_editor_source()) provenance = None if prov != "none": provenance = prov == "true" thenote = None if note == "yes": thenote = "abcdefg" ######################################## ## execute svc = DOAJ.applicationService() if raises is not None and raises != "": with self.assertRaises(EXCEPTIONS[raises]): svc.reject_application(ap, acc, provenance, note=thenote) else: svc.reject_application(ap, acc, provenance, note=thenote) time.sleep(1) ####################################### ## Check ap2 = Suggestion.pull(ap.id) assert ap2 is not None assert ap2.application_status == constants.APPLICATION_STATUS_REJECTED assert ap2.current_journal is None # check the updated and manually updated date are essentially the same (they can theoretically differ # by a small amount just based on when they are set) updated_spread = abs((ap2.last_updated_timestamp - ap2.last_manual_update_timestamp).total_seconds()) assert updated_spread <= 1.0 if current_journal == "yes" and journal is not None: j2 = Journal.pull(journal.id) assert j2 is not None assert j2.current_application is None assert ap2.related_journal == j2.id if prov == "true": pr = Provenance.get_latest_by_resource_id(ap.id) assert pr is not None if note == "yes": assert len(ap2.notes) == 1 assert ap2.notes[0].get("note") == "abcdefg" elif note == "no": assert len(ap2.notes) == 0
def test_02_application_2_journal(self, name, application_type, manual_update_arg, app_key_properties, current_journal, raises): # set up for the test ######################################### cj = None has_seal = bool(randint(0, 1)) application = None if application_type == "present": application = Suggestion(**ApplicationFixtureFactory.make_application_source()) application.set_id(application.makeid()) application.remove_contacts() application.remove_editor_group() application.remove_editor() application.remove_owner() application.remove_current_journal() application.remove_notes() if app_key_properties == "yes": application.add_contact("Application", "*****@*****.**") application.set_editor_group("appeditorgroup") application.set_editor("appeditor") application.set_owner("appowner") application.set_seal(has_seal) application.add_note("Application Note") if current_journal == "present": journal = Journal(**JournalFixtureFactory.make_journal_source()) journal.remove_contacts() journal.add_contact("Journal", "*****@*****.**") journal.set_editor_group("journaleditorgroup") journal.set_editor("journaleditor") journal.set_owner("journalowner") journal.remove_current_application() journal.remove_notes() journal.add_note("Journal Note") journal.save(blocking=True) application.set_current_journal(journal.id) cj = journal elif current_journal == "missing": application.set_current_journal("123456789987654321") manual_update = None if manual_update_arg == "true": manual_update = True elif manual_update_arg == "false": manual_update = False # execute the test ######################################## svc = DOAJ.applicationService() if raises is not None and raises != "": with self.assertRaises(EXCEPTIONS[raises]): svc.application_2_journal(application, manual_update) else: journal = svc.application_2_journal(application, manual_update) # check the result ###################################### assert journal is not None assert isinstance(journal, Journal) assert journal.is_in_doaj() is True jbj = journal.bibjson().data del jbj["active"] assert jbj == application.bibjson().data if current_journal == "present": assert len(journal.related_applications) == 3 else: assert len(journal.related_applications) == 1 related = journal.related_application_record(application.id) assert related is not None if manual_update_arg == "true": assert journal.last_manual_update is not None and journal.last_manual_update != "1970-01-01T00:00:00Z" if app_key_properties == "yes": contacts = journal.contacts() assert len(contacts) == 1 assert contacts[0].get("name") == "Application" assert contacts[0].get("email") == "*****@*****.**" assert journal.editor_group == "appeditorgroup" assert journal.editor == "appeditor" assert journal.owner == "appowner" assert journal.has_seal() == has_seal if current_journal == "present": assert len(journal.notes) == 2 else: assert len(journal.notes) == 1 elif app_key_properties == "no": if current_journal == "present": contacts = journal.contacts() assert len(contacts) == 1 assert contacts[0].get("name") == "Journal" assert contacts[0].get("email") == "*****@*****.**" assert journal.editor_group == "journaleditorgroup" assert journal.editor == "journaleditor" assert journal.owner == "journalowner" assert journal.has_seal() == has_seal assert len(journal.notes) == 2 elif current_journal == "none" or current_journal == "missing": contacts = journal.contacts() assert len(contacts) == 0 assert journal.editor_group is None assert journal.editor is None assert journal.owner is None assert journal.has_seal() == has_seal assert len(journal.notes) == 1 if current_journal == "present": assert cj.id == journal.id assert cj.created_date == journal.created_date
def test_01_accept_application(self, name, application_type, account_type, manual_update, provenance, raises, result_provenance, result_manual_update): ############################################### ## set up # create the application application = None if application_type == "save_fail": application = Suggestion(**ApplicationFixtureFactory.make_application_source()) application.save = mock_save Journal.save = mock_save elif application_type == "with_current_journal": application = Suggestion(**ApplicationFixtureFactory.make_application_source()) application.remove_notes() application.add_note("unique 1", "2002-01-01T00:00:00Z") application.add_note("duplicate", "2001-01-01T00:00:00Z") cj = application.current_journal journal = Journal(**JournalFixtureFactory.make_journal_source()) journal.set_id(cj) journal.remove_notes() journal.add_note("unique 2", "2003-01-01T00:00:00Z") journal.add_note("duplicate", "2001-01-01T00:00:00Z") journal.save(blocking=True) elif application_type == "no_current_journal": application = Suggestion(**ApplicationFixtureFactory.make_application_source()) application.remove_current_journal() acc = None if account_type == "not_allowed": acc = Account(**AccountFixtureFactory.make_publisher_source()) elif account_type == "allowed": acc = Account(**AccountFixtureFactory.make_managing_editor_source()) mu = None if manual_update in ["true", "false"]: mu = manual_update == "true" prov = None if provenance in ["true", "false"]: prov = provenance == "true" save = bool(randint(0,1)) ########################################################### # Execution svc = DOAJ.applicationService() if raises != "": with self.assertRaises(EXCEPTIONS[raises]): svc.accept_application(application, acc, mu, prov) else: journal = svc.accept_application(application, acc, mu, prov, save_journal=save, save_application=save) # we need to sleep, so the index catches up time.sleep(1) # check a few common things assert application.application_status == constants.APPLICATION_STATUS_ACCEPTED assert application.current_journal is None assert journal.current_application is None assert application.related_journal == journal.id related = journal.related_applications if application_type == "with_current_journal": assert len(related) == 3 elif application_type == "no_current_journal": assert len(related) == 1 assert related[0].get("application_id") == application.id assert related[0].get("date_accepted") is not None if result_manual_update == "yes": assert journal.last_manual_update is not None assert journal.last_manual_update != "1970-01-01T00:00:00Z" assert application.last_manual_update is not None assert application.last_manual_update != "1970-01-01T00:00:00Z" elif result_manual_update == "no": assert journal.last_manual_update is None assert application.last_manual_update is None if application_type == "with_current_journal": assert len(journal.notes) == 3 notevals = [note.get("note") for note in journal.notes] assert "duplicate" in notevals assert "unique 1" in notevals assert "unique 2" in notevals app_prov = Provenance.get_latest_by_resource_id(application.id) if result_provenance == "yes": assert app_prov is not None elif result_provenance == "no": assert app_prov is None if save: pass
def update(cls, id, data, account): # as long as authentication (in the layer above) has been successful, and the account exists, then # we are good to proceed if account is None: raise Api401Error() # next thing to do is a structural validation of the replacement data, by instantiating the object try: ia = IncomingApplication(data) except dataobj.DataStructureException as e: raise Api400Error(e.message) # now see if there's something for us to update ap = models.Suggestion.pull(id) if ap is None: raise Api404Error() # if that works, convert it to a Suggestion object new_ap = ia.to_application_model() # now augment the suggestion object with all the additional information it requires # # suggester name and email from the user account new_ap.set_suggester(account.name, account.email) # they are not allowed to set "subject" new_ap.bibjson().remove_subjects() # DOAJ BLL for this request applicationService = DOAJ.applicationService() authService = DOAJ.authorisationService() # if a current_journal is specified on the incoming data if new_ap.current_journal is not None: # once an application has a current_journal specified, you can't change it if new_ap.current_journal != ap.current_journal: raise Api400Error("current_journal cannot be changed once set. current_journal is {x}; this request tried to change it to {y}".format(x=ap.current_journal, y=new_ap.current_journal)) # load the update_request application either directly or by crosswalking the journal object vanilla_ap = None jlock = None alock = None try: vanilla_ap, jlock, alock = applicationService.update_request_for_journal(new_ap.current_journal, account=account) except AuthoriseException as e: if e.reason == AuthoriseException.WRONG_STATUS: raise Api403Error("The application is no longer in a state in which it can be edited via the API") else: raise Api404Error() except lock.Locked as e: raise Api409Error("The application is locked for editing by another user - most likely your application is being reviewed by an editor") # if we didn't find an application or journal, 404 the user if vanilla_ap is None: if jlock is not None: jlock.delete() if alock is not None: alock.delete() raise Api404Error() # convert the incoming application into the web form form = MultiDict(xwalk.SuggestionFormXWalk.obj2form(new_ap)) fc = formcontext.ApplicationFormFactory.get_form_context(role="publisher", form_data=form, source=vanilla_ap) if fc.validate(): try: fc.finalise(email_alert=False) return fc.target except formcontext.FormContextException as e: raise Api400Error(e.message) finally: if jlock is not None: jlock.delete() if alock is not None: alock.delete() else: if jlock is not None: jlock.delete() if alock is not None: alock.delete() raise Api400Error(cls._validation_message(fc)) else: try: authService.can_edit_application(account, ap) except AuthoriseException as e: if e.reason == e.WRONG_STATUS: raise Api403Error("The application is no longer in a state in which it can be edited via the API") else: raise Api404Error() # convert the incoming application into the web form form = MultiDict(xwalk.SuggestionFormXWalk.obj2form(new_ap)) fc = formcontext.ApplicationFormFactory.get_form_context(form_data=form, source=ap) if fc.validate(): try: fc.finalise(email_alert=False) return fc.target except formcontext.FormContextException as e: raise Api400Error(e.message) else: raise Api400Error(cls._validation_message(fc))
def test_01_discover_duplicates(self, name, kwargs): article_arg = kwargs.get("article") owner_arg = kwargs.get("owner") article_doi_arg = kwargs.get("article_doi") doi_duplicate_arg = kwargs.get("doi_duplicate") article_fulltext_arg = kwargs.get("article_fulltext") fulltext_duplicate_arg = kwargs.get("fulltext_duplicate") articles_by_doi_arg = kwargs.get("articles_by_doi") articles_by_fulltext_arg = kwargs.get("articles_by_fulltext") raises_arg = kwargs.get("raises") raises = EXCEPTIONS.get(raises_arg) ############################################### ## set up owner = None if owner_arg != "none": owner = Account(**AccountFixtureFactory.make_publisher_source()) owner_id = None if owner is not None: owner_id = owner.id # create a journal for the owner if owner_arg not in ["none"]: source = JournalFixtureFactory.make_journal_source(in_doaj=True) journal = Journal(**source) journal.set_owner(owner.id) journal.bibjson().remove_identifiers() journal.bibjson().add_identifier("eissn", "1234-5678") journal.bibjson().add_identifier("pissn", "9876-5432") journal.save() # determine what we need to load into the index article_ids = [] aids_block = [] if owner_arg not in ["none", "no_articles"]: for i, ident in enumerate(IDENTS): the_doi = ident["doi"] if doi_duplicate_arg == "padded": the_doi = " " + the_doi + " " elif doi_duplicate_arg == "prefixed": the_doi = "https://dx.doi.org/" + the_doi the_fulltext = ident["fulltext"] if article_fulltext_arg != "invalid": if fulltext_duplicate_arg == "padded": the_fulltext = " http:" + the_fulltext elif fulltext_duplicate_arg == "http": the_fulltext = "http:" + the_fulltext elif fulltext_duplicate_arg == "https": the_fulltext = "https:" + the_fulltext else: the_fulltext = "http:" + the_fulltext source = ArticleFixtureFactory.make_article_source( eissn="1234-5678", pissn="9876-5432", doi=the_doi, fulltext=the_fulltext) article = Article(**source) article.set_id() article.save(blocking=True) article_ids.append(article.id) aids_block.append((article.id, article.last_updated)) # generate our incoming article article = None doi = None fulltext = None if article_arg == "yes": eissn = "1234=5678" # one matching pissn = "6789-1234" # the other not - issn matches are not relevant to this test if article_doi_arg in ["yes", "padded"]: doi = "10.1234/abc/11" if doi_duplicate_arg in ["yes", "padded"]: doi = IDENTS[0]["doi"] if article_doi_arg == "padded": doi = " doi:" + doi + " " elif article_doi_arg in ["invalid"]: doi = IDENTS[-1]["doi"] if article_fulltext_arg in ["yes", "padded", "https"]: fulltext = "//example.com/11" if fulltext_duplicate_arg in ["yes", "padded", "https"]: fulltext = IDENTS[0]["fulltext"] if fulltext_duplicate_arg == "padded": fulltext = " http:" + fulltext + " " elif fulltext_duplicate_arg == "https": fulltext = "https:" + fulltext else: fulltext = "http:" + fulltext elif article_fulltext_arg == "invalid": fulltext = IDENTS[-1]["fulltext"] source = ArticleFixtureFactory.make_article_source( eissn=eissn, pissn=pissn, doi=doi, fulltext=fulltext) article = Article(**source) # we need to do this if doi or fulltext are none, because the factory will set a default if we don't # provide them if doi is None: article.bibjson().remove_identifiers("doi") if fulltext is None: article.bibjson().remove_urls("fulltext") article.set_id() Article.blockall(aids_block) ########################################################### # Execution svc = DOAJ.articleService() if raises is not None: with self.assertRaises(raises): svc.discover_duplicates(article) else: possible_articles = svc.discover_duplicates(article) if articles_by_doi_arg == "yes": assert "doi" in possible_articles assert len(possible_articles["doi"]) == 1 # if this is the "invalid" doi, then we expect it to match the final article, otherwise match the first if article_doi_arg == "invalid": assert possible_articles["doi"][0].id == article_ids[-1] else: assert possible_articles["doi"][0].id == article_ids[0] else: if possible_articles is not None: assert "doi" not in possible_articles if articles_by_fulltext_arg == "yes": assert "fulltext" in possible_articles assert len(possible_articles["fulltext"]) == 1 # if this is the "invalid" fulltext url, then we expect it to match the final article, otherwise match the first if article_fulltext_arg == "invalid": assert possible_articles["fulltext"][0].id == article_ids[ -1] else: assert possible_articles["fulltext"][0].id == article_ids[ 0] else: if possible_articles is not None: assert "fulltext" not in possible_articles
def setUp(self): super(TestBLLArticleGetDuplicates, self).setUp() self.svc = DOAJ.articleService() self._old_discover_duplicates = self.svc.discover_duplicates
def test_01_delete_application(self, name, application_type, account_type, current_journal, related_journal, raises): ############################################### ## set up # create the test application (if needed), and the associated current_journal and related_journal in suitable states application = None cj = None rj = None if application_type == "found" or application_type == "locked": application = Suggestion(**ApplicationFixtureFactory.make_application_source()) if current_journal == "none": application.remove_current_journal() elif current_journal == "not_found": application.set_current_journal("123456789987654321") elif current_journal == "found": cj = Journal(**JournalFixtureFactory.make_journal_source()) cj.set_id(cj.makeid()) cj.save(blocking=True) application.set_current_journal(cj.id) elif current_journal == "locked": cj = Journal(**JournalFixtureFactory.make_journal_source()) cj.set_id(cj.makeid()) cj.save(blocking=True) application.set_current_journal(cj.id) lock.lock(constants.LOCK_JOURNAL, cj.id, "otheruser") if related_journal == "none": application.remove_related_journal() elif related_journal == "not_found": application.set_related_journal("123456789987654321") elif related_journal == "found": rj = Journal(**JournalFixtureFactory.make_journal_source()) rj.set_id(rj.makeid()) rj.save(blocking=True) application.set_related_journal(rj.id) elif related_journal == "locked": rj = Journal(**JournalFixtureFactory.make_journal_source()) rj.set_id(rj.makeid()) rj.save(blocking=True) application.set_related_journal(rj.id) lock.lock(constants.LOCK_JOURNAL, rj.id, "otheruser") acc = None if account_type != "none": acc = Account(**AccountFixtureFactory.make_publisher_source()) if account_type == "not_permitted": acc.remove_role("publisher") if application_type == "locked": thelock = lock.lock(constants.LOCK_APPLICATION, application.id, "otheruser") # we can't explicitly block on the lock, but we can halt until we confirm it is saved thelock.blockall([(thelock.id, thelock.last_updated)]) application_id = None if application is not None: if acc is not None: application.set_owner(acc.id) application.save(blocking=True) application_id = application.id elif application_type == "not_found": application_id = u"sdjfasofwefkwflkajdfasjd" ########################################################### # Execution svc = DOAJ.applicationService() if raises != "": with self.assertRaises(EXCEPTIONS[raises]): svc.delete_application(application_id, acc) time.sleep(1) check_locks(application, cj, rj, acc) else: svc.delete_application(application_id, acc) # we need to sleep, so the index catches up time.sleep(1) # check that no locks remain set for this user check_locks(application, cj, rj, acc) # check that the application actually is gone if application is not None: assert Suggestion.pull(application.id) is None # check that the current journal no longer has a reference to the application if cj is not None: cj = Journal.pull(cj.id) assert cj.current_application is None # check that the related journal has a record that the application was deleted if rj is not None: rj = Journal.pull(rj.id) record = rj.related_application_record(application.id) assert "status" in record assert record["status"] == "deleted"
def test_01_issn_ownership_status(self, name, kwargs): article_arg = kwargs.get("article") owner_arg = kwargs.get("owner") article_eissn_arg = kwargs.get("article_eissn") article_pissn_arg = kwargs.get("article_pissn") seen_eissn_arg = kwargs.get("seen_eissn") seen_pissn_arg = kwargs.get("seen_pissn") journal_owner_arg = kwargs.get("journal_owner") raises_arg = kwargs.get("raises") raises = EXCEPTIONS.get(raises_arg) ############################################### ## set up owner = None if owner_arg != "none": owner = Account(**AccountFixtureFactory.make_publisher_source()) owner_id = None if owner is not None: owner_id = owner.id # generate our incoming article article = None eissn = None pissn = None if article_arg == "exists": source = ArticleFixtureFactory.make_article_source() article = Article(**source) article.set_id() article.bibjson().remove_identifiers("pissn") if article_pissn_arg == "yes": pissn = "1234-5678" article.bibjson().add_identifier("pissn", pissn) article.bibjson().remove_identifiers("eissn") if article_eissn_arg == "yes": eissn = "9876-5432" article.bibjson().add_identifier("eissn", eissn) issns = [] if eissn is not None and pissn is not None and seen_eissn_arg == "yes" and seen_pissn_arg == "yes": issns.append((eissn, pissn)) if eissn is not None and seen_eissn_arg == "yes": issns.append((eissn, "4321-9876")) issns.append((eissn, None)) if pissn is not None and seen_pissn_arg == "yes": issns.append(("6789-4321", pissn)) issns.append((None, pissn)) owners = [] if journal_owner_arg == "none": owners = [None] elif journal_owner_arg == "correct" and owner_id is not None: owners = [owner_id] elif journal_owner_arg == "incorrect": owners = ["randomowner"] elif journal_owner_arg == "mix" and owner_id is not None: owners.append(owner_id) owners.append("randomowner") owners.append(None) mock = ModelJournalMockFactory.find_by_issn(issns, owners) Journal.find_by_issn = mock ########################################################### # Execution svc = DOAJ.articleService() if raises is not None: with self.assertRaises(raises): svc.issn_ownership_status(article, owner_id) else: owned, shared, unowned, unmatched = svc.issn_ownership_status( article, owner_id) owned_count = 0 if seen_eissn_arg == "yes" and eissn is not None and journal_owner_arg in [ "correct" ]: assert eissn in owned owned_count += 1 elif eissn is not None: assert eissn not in owned if seen_pissn_arg == "yes" and pissn is not None and journal_owner_arg in [ "correct" ]: assert pissn in owned owned_count += 1 elif pissn is not None: assert pissn not in owned assert len(owned) == owned_count shared_count = 0 if seen_eissn_arg == "yes" and eissn is not None and journal_owner_arg in [ "mix" ]: assert eissn in shared shared_count += 1 elif eissn is not None: assert eissn not in shared if seen_pissn_arg == "yes" and pissn is not None and journal_owner_arg in [ "mix" ]: assert pissn in shared shared_count += 1 elif pissn is not None: assert pissn not in shared assert len(shared) == shared_count unowned_count = 0 if seen_eissn_arg == "yes" and eissn is not None and journal_owner_arg in [ "incorrect", "none" ]: assert eissn in unowned unowned_count += 1 elif eissn is not None: assert eissn not in unowned if seen_pissn_arg == "yes" and pissn is not None and journal_owner_arg in [ "incorrect", "none" ]: assert pissn in unowned unowned_count += 1 elif pissn is not None: assert pissn not in unowned assert len(unowned) == unowned_count unmatched_count = 0 if seen_eissn_arg == "no" and eissn is not None: assert eissn in unmatched unmatched_count += 1 elif eissn is not None: assert eissn not in unmatched if seen_pissn_arg == "no" and pissn is not None: assert pissn in unmatched unmatched_count += 1 elif pissn is not None: assert pissn not in unmatched assert len(unmatched) == unmatched_count
def update(cls, id, data, account): # as long as authentication (in the layer above) has been successful, and the account exists, then # we are good to proceed if account is None: raise Api401Error() # next thing to do is a structural validation of the replacement data, by instantiating the object try: ia = IncomingApplication(data) except dataobj.DataStructureException as e: raise Api400Error(e.message) # now see if there's something for us to update ap = models.Suggestion.pull(id) if ap is None: raise Api404Error() # if that works, convert it to a Suggestion object new_ap = ia.to_application_model() # now augment the suggestion object with all the additional information it requires # # suggester name and email from the user account new_ap.set_suggester(account.name, account.email) # they are not allowed to set "subject" new_ap.bibjson().remove_subjects() # DOAJ BLL for this request applicationService = DOAJ.applicationService() authService = DOAJ.authorisationService() # if a current_journal is specified on the incoming data if new_ap.current_journal is not None: # once an application has a current_journal specified, you can't change it if new_ap.current_journal != ap.current_journal: raise Api400Error( "current_journal cannot be changed once set. current_journal is {x}; this request tried to change it to {y}" .format(x=ap.current_journal, y=new_ap.current_journal)) # load the update_request application either directly or by crosswalking the journal object vanilla_ap = None jlock = None alock = None try: vanilla_ap, jlock, alock = applicationService.update_request_for_journal( new_ap.current_journal, account=account) except AuthoriseException as e: if e.reason == AuthoriseException.WRONG_STATUS: raise Api403Error( "The application is no longer in a state in which it can be edited via the API" ) else: raise Api404Error() except lock.Locked as e: raise Api409Error( "The application is locked for editing by another user - most likely your application is being reviewed by an editor" ) # if we didn't find an application or journal, 404 the user if vanilla_ap is None: if jlock is not None: jlock.delete() if alock is not None: alock.delete() raise Api404Error() # convert the incoming application into the web form form = MultiDict(xwalk.SuggestionFormXWalk.obj2form(new_ap)) fc = formcontext.ApplicationFormFactory.get_form_context( role="publisher", form_data=form, source=vanilla_ap) if fc.validate(): try: fc.finalise(email_alert=False) return fc.target except formcontext.FormContextException as e: raise Api400Error(e.message) finally: if jlock is not None: jlock.delete() if alock is not None: alock.delete() else: if jlock is not None: jlock.delete() if alock is not None: alock.delete() raise Api400Error(cls._validation_message(fc)) else: try: authService.can_edit_application(account, ap) except AuthoriseException as e: if e.reason == e.WRONG_STATUS: raise Api403Error( "The application is no longer in a state in which it can be edited via the API" ) else: raise Api404Error() # convert the incoming application into the web form form = MultiDict(xwalk.SuggestionFormXWalk.obj2form(new_ap)) fc = formcontext.ApplicationFormFactory.get_form_context( form_data=form, source=ap) if fc.validate(): try: fc.finalise(email_alert=False) return fc.target except formcontext.FormContextException as e: raise Api400Error(e.message) else: raise Api400Error(cls._validation_message(fc))
def test_01_update_request(self, name, journal_id, journal_lock, account, account_role, account_is_owner, current_applications, application_lock, application_status, completed_applications, raises, return_app, return_jlock, return_alock, db_jlock, db_alock, db_app): ############################################### ## set up # create the journal journal = None jid = None if journal_id == "valid": journal = Journal(**JournalFixtureFactory.make_journal_source( in_doaj=True)) journal.remove_related_applications() journal.remove_current_application() jid = journal.id elif journal_id == "not_in_doaj": journal = Journal(**JournalFixtureFactory.make_journal_source( in_doaj=False)) journal.remove_related_applications() journal.remove_current_application() jid = journal.id elif journal_id == "missing": jid = uuid.uuid4().hex acc = None if account == "yes": acc = Account(**AccountFixtureFactory.make_publisher_source()) if account_role == "none": acc.remove_role("publisher") elif account_role == "admin": acc.remove_role("publisher") acc.add_role("admin") acc.set_id(acc.makeid()) if account_is_owner == "yes": acc.set_id(journal.owner) if journal_lock == "yes": lock.lock("journal", jid, "someoneelse", blocking=True) latest_app = None current_app_count = int(current_applications) for i in range(current_app_count): app = Suggestion( **ApplicationFixtureFactory.make_application_source()) app.set_id(app.makeid()) app.set_created("198" + str(i) + "-01-01T00:00:00Z") app.set_current_journal(jid) app.save() latest_app = app if journal is not None: journal.set_current_application(app.id) comp_app_count = int(completed_applications) for i in range(comp_app_count): app = Suggestion( **ApplicationFixtureFactory.make_application_source()) app.set_id(app.makeid()) app.set_created("197" + str(i) + "-01-01T00:00:00Z") app.set_related_journal(jid) app.save() if journal is not None: journal.add_related_application(app.id, date_accepted=app.created_date) if current_app_count == 0 and comp_app_count == 0: # save at least one record to initialise the index mapping, otherwise tests fail app = Suggestion( **ApplicationFixtureFactory.make_application_source()) app.set_id(app.makeid()) app.save() if application_lock == "yes": lock.lock("suggestion", latest_app.id, "someoneelse", blocking=True) if application_status != "n/a": latest_app.set_application_status(application_status) latest_app.save(blocking=True) # finally save the journal record, ensuring we get a blocking save, so everything # above here should be synchronised with the repo if journal is not None: journal.save(blocking=True) ########################################################### # Execution svc = DOAJ.applicationService() if raises != "": with self.assertRaises(EXCEPTIONS[raises]): svc.update_request_for_journal(jid, acc) else: application, jlock, alock = svc.update_request_for_journal( jid, acc) # we need to sleep, so the index catches up time.sleep(1) if return_app == "none": assert application is None elif return_app == "yes": assert application is not None if return_jlock == "none": assert jlock is None elif return_jlock == "yes": assert jlock is not None if return_alock == "none": assert alock is None elif return_alock == "yes": assert alock is not None if db_jlock == "no" and acc is not None: assert not lock.has_lock("journal", jid, acc.id) elif db_jlock == "yes" and acc is not None: l = lock.has_lock("journal", jid, acc.id) assert lock.has_lock("journal", jid, acc.id) if db_alock == "no" and application.id is not None and acc is not None: assert not lock.has_lock("suggestion", application.id, acc.id) elif db_alock == "yes" and application.id is not None and acc is not None: assert lock.has_lock("suggestion", application.id, acc.id) if db_app == "no" and application.id is not None: indb = Suggestion.q2obj(q="id.exact:" + application.id) assert indb is None elif db_app == "yes" and application.id is not None: indb = Suggestion.q2obj(q="id.exact:" + application.id) assert indb is not None if current_app_count == 0 and comp_app_count == 0 and application is not None: assert application.article_metadata is None assert application.articles_last_year is None elif application is not None: assert application.article_metadata is not None assert application.articles_last_year is not None
def create(cls, data, account, dry_run=False): # as long as authentication (in the layer above) has been successful, and the account exists, then # we are good to proceed if account is None: raise Api401Error() # first thing to do is a structural validation, but instantiating the data object try: ia = IncomingApplication(data) except dataobj.DataStructureException as e: raise Api400Error(e.message) # if that works, convert it to a Suggestion object ap = ia.to_application_model() # now augment the suggestion object with all the additional information it requires # # suggester name and email from the user account ap.set_suggester(account.name, account.email) # they are not allowed to set "subject" ap.bibjson().remove_subjects() # if this is an update request on an existing journal if ap.current_journal is not None: # DOAJ BLL for this request applicationService = DOAJ.applicationService() # load the update_request application either directly or by crosswalking the journal object vanilla_ap = None jlock = None alock = None try: vanilla_ap, jlock, alock = applicationService.update_request_for_journal( ap.current_journal, account=account) except AuthoriseException as e: if e.reason == AuthoriseException.WRONG_STATUS: raise Api403Error( "The application is no longer in a state in which it can be edited via the API" ) else: raise Api404Error() except lock.Locked as e: raise Api409Error( "The application you are requesting an update for is locked for editing by another user" ) # if we didn't find an application or journal, 404 the user if vanilla_ap is None: if jlock is not None: jlock.delete() if alock is not None: alock.delete() raise Api404Error() # convert the incoming application into the web form form = MultiDict(xwalk.SuggestionFormXWalk.obj2form(ap)) fc = formcontext.ApplicationFormFactory.get_form_context( role="publisher", form_data=form, source=vanilla_ap) if fc.validate(): try: save_target = not dry_run fc.finalise(save_target=save_target, email_alert=False) return fc.target except formcontext.FormContextException as e: raise Api400Error(e.message) finally: if jlock is not None: jlock.delete() if alock is not None: alock.delete() else: if jlock is not None: jlock.delete() if alock is not None: alock.delete() raise Api400Error(cls._validation_message(fc)) # otherwise, this is a brand-new application else: # convert the incoming application into the web form form = MultiDict(xwalk.SuggestionFormXWalk.obj2form(ap)) # create a template that will hold all the values we want to persist across the form submission template = models.Suggestion() template.set_owner(account.id) fc = formcontext.ApplicationFormFactory.get_form_context( form_data=form, source=template) if fc.validate(): try: save_target = not dry_run fc.finalise(save_target=save_target, email_alert=False) return fc.target except formcontext.FormContextException as e: raise Api400Error(e.message) else: raise Api400Error(cls._validation_message(fc))
def test_01_update_request(self, name, journal_id, journal_lock, account, account_role, account_is_owner, current_applications, application_lock, application_status, completed_applications, raises, return_app, return_jlock, return_alock, db_jlock, db_alock, db_app): ############################################### ## set up # create the journal journal = None jid = None if journal_id == "valid": journal = Journal(**JournalFixtureFactory.make_journal_source(in_doaj=True)) journal.remove_related_applications() journal.remove_current_application() jid = journal.id elif journal_id == "not_in_doaj": journal = Journal(**JournalFixtureFactory.make_journal_source(in_doaj=False)) journal.remove_related_applications() journal.remove_current_application() jid = journal.id elif journal_id == "missing": jid = uuid.uuid4().hex acc = None if account == "yes": acc = Account(**AccountFixtureFactory.make_publisher_source()) if account_role == "none": acc.remove_role("publisher") elif account_role == "admin": acc.remove_role("publisher") acc.add_role("admin") acc.set_id(acc.makeid()) if account_is_owner == "yes": acc.set_id(journal.owner) if journal_lock == "yes": lock.lock("journal", jid, "someoneelse", blocking=True) latest_app = None current_app_count = int(current_applications) for i in range(current_app_count): app = Suggestion(**ApplicationFixtureFactory.make_application_source()) app.set_id(app.makeid()) app.set_created("198" + str(i) + "-01-01T00:00:00Z") app.set_current_journal(jid) app.save() latest_app = app if journal is not None: journal.set_current_application(app.id) comp_app_count = int(completed_applications) for i in range(comp_app_count): app = Suggestion(**ApplicationFixtureFactory.make_application_source()) app.set_id(app.makeid()) app.set_created("197" + str(i) + "-01-01T00:00:00Z") app.set_related_journal(jid) app.save() if journal is not None: journal.add_related_application(app.id, date_accepted=app.created_date) if current_app_count == 0 and comp_app_count == 0: # save at least one record to initialise the index mapping, otherwise tests fail app = Suggestion(**ApplicationFixtureFactory.make_application_source()) app.set_id(app.makeid()) app.save() if application_lock == "yes": lock.lock("suggestion", latest_app.id, "someoneelse", blocking=True) if application_status != "n/a": latest_app.set_application_status(application_status) latest_app.save(blocking=True) # finally save the journal record, ensuring we get a blocking save, so everything # above here should be synchronised with the repo if journal is not None: journal.save(blocking=True) ########################################################### # Execution svc = DOAJ.applicationService() if raises != "": with self.assertRaises(EXCEPTIONS[raises]): svc.update_request_for_journal(jid, acc) else: application, jlock, alock = svc.update_request_for_journal(jid, acc) # we need to sleep, so the index catches up time.sleep(1) if return_app == "none": assert application is None elif return_app == "yes": assert application is not None if return_jlock == "none": assert jlock is None elif return_jlock == "yes": assert jlock is not None if return_alock == "none": assert alock is None elif return_alock == "yes": assert alock is not None if db_jlock == "no" and acc is not None: assert not lock.has_lock("journal", jid, acc.id) elif db_jlock == "yes" and acc is not None: assert lock.has_lock("journal", jid, acc.id) if db_alock == "no" and application.id is not None and acc is not None: assert not lock.has_lock("suggestion", application.id, acc.id) elif db_alock == "yes" and application.id is not None and acc is not None: assert lock.has_lock("suggestion", application.id, acc.id) if db_app == "no" and application.id is not None: indb = Suggestion.q2obj(q="id.exact:" + application.id) assert indb is None elif db_app == "yes" and application.id is not None: indb = Suggestion.q2obj(q="id.exact:" + application.id) assert indb is not None if current_app_count == 0 and comp_app_count == 0 and application is not None: assert application.article_metadata is None assert application.articles_last_year is None elif application is not None: assert application.article_metadata is not None assert application.articles_last_year is not None