def to_application_model(self, existing=None): nd = deepcopy(self.data) # we need to re-write the archiving policy section ap = nd.get("bibjson", {}).get("archiving_policy") if ap is not None: nap = {} if "url" in ap: nap["url"] = ap["url"] if "policy" in ap: known = [] for pol in ap["policy"]: if "domain" in pol: if pol.get("domain").lower() == "other": nap["other"] = pol.get("name") elif pol.get("domain").lower() == "a national library": nap["nat_lib"] = pol.get("name") else: known.append(pol.get("name")) if len(known) > 0: nap["known"] = known nd["bibjson"]["archiving_policy"] = nap if existing is None: return models.Suggestion(**nd) else: nnd = dataobj.merge_outside_construct(self._struct, nd, existing.data) return models.Suggestion(**nnd)
def test_31_application_latest_by_current_journal(self): j = models.Journal() j.set_id(j.makeid()) app1 = models.Suggestion( **ApplicationFixtureFactory.make_application_source()) app1.set_id(app1.makeid()) app1.set_current_journal(j.id) app1.set_created("1970-01-01T00:00:00Z") app1.save() app2 = models.Suggestion( **ApplicationFixtureFactory.make_application_source()) app2.set_id(app2.makeid()) app2.set_current_journal(j.id) app2.set_created("1971-01-01T00:00:00Z") app2.save(blocking=True) # check that we find the right application when we search app3 = models.Suggestion.find_latest_by_current_journal(j.id) assert app3 is not None assert app3.id == app2.id # make sure we get a None response when there's no application app0 = models.Suggestion.find_latest_by_current_journal("whatever") assert app0 is None
def test_02_classification_required(self): # Check we can mark an application 'completed' with a subject classification present in_progress_application = models.Suggestion( **ApplicationFixtureFactory.make_application_source()) in_progress_application.set_application_status( constants.APPLICATION_STATUS_IN_PROGRESS) fc = formcontext.ApplicationFormFactory.get_form_context( role='associate_editor', source=in_progress_application) # Make changes to the application status via the form, check it validates fc.form.application_status.data = constants.APPLICATION_STATUS_COMPLETED assert fc.validate() # Without a subject classification, we should not be able to set the status to 'completed' no_class_application = models.Suggestion( **ApplicationFixtureFactory.make_application_source()) del no_class_application.data['bibjson']['subject'] fc = formcontext.ApplicationFormFactory.get_form_context( role='associate_editor', source=no_class_application) # Make changes to the application status via the form assert fc.source.bibjson().subjects() == [] fc.form.application_status.data = constants.APPLICATION_STATUS_COMPLETED assert not fc.validate() # However, we should be able to set it to a different status rather than 'completed' fc.form.application_status.data = constants.APPLICATION_STATUS_PENDING assert fc.validate()
def test_03_classification_required(self): # Check we can accept an application with a subject classification present ready_application = models.Suggestion(**ApplicationFixtureFactory.make_application_source()) ready_application.set_application_status(constants.APPLICATION_STATUS_READY) fc = formcontext.ApplicationFormFactory.get_form_context(role='admin', source=ready_application) # Make changes to the application status via the form, check it validates fc.form.application_status.data = constants.APPLICATION_STATUS_ACCEPTED assert fc.validate() # Without a subject classification, we should not be able to set the status to 'accepted' no_class_application = models.Suggestion(**ApplicationFixtureFactory.make_application_source()) del no_class_application.data['bibjson']['subject'] fc = formcontext.ApplicationFormFactory.get_form_context(role='admin', source=no_class_application) # Make changes to the application status via the form assert fc.source.bibjson().subjects() == [] fc.form.application_status.data = constants.APPLICATION_STATUS_ACCEPTED assert not fc.validate() # However, we should be able to set it to a different status rather than 'accepted' fc.form.application_status.data = constants.APPLICATION_STATUS_IN_PROGRESS assert fc.validate()
def test_01_publisher_csv_reapplication_success(self): """Give the publisher's csv reapplication a full workout""" # we start by constructing it from source fc = formcontext.ApplicationFormFactory.get_form_context( role="csv", source=models.Suggestion(**REAPPLICATION_SOURCE)) assert isinstance(fc, formcontext.PublisherCsvReApplication) assert fc.form is not None assert fc.source is not None assert fc.form_data is None assert fc.template is None # because no template is set # now construct it from form data (with a known source) fc = formcontext.ApplicationFormFactory.get_form_context( role="csv", form_data=MultiDict(REAPPLICATION_FORM), source=models.Suggestion(**REAPPLICATION_SOURCE)) assert isinstance(fc, formcontext.PublisherCsvReApplication) assert fc.form is not None assert fc.source is not None assert fc.form_data is not None # test each of the workflow components individually ... # pre-validate and ensure that the disbaled fields get re-set fc.pre_validate() assert fc.form.pissn.data == "1234-5678" assert fc.form.eissn.data == "9876-5432" assert fc.form.contact_name.data == "Contact Name" assert fc.form.contact_email.data == "*****@*****.**" assert fc.form.confirm_contact_email.data == "*****@*****.**" # run the validation itself assert fc.validate(), fc.form.errors # run the crosswalk (no need to look in detail, xwalks are tested elsewhere) fc.form2target() assert fc.target is not None # patch the target with data from the source fc.patch_target() assert fc.target.created_date == "2000-01-01T00:00:00Z" assert fc.target.id == "abcdefghijk" assert len(fc.target.notes()) == 2 assert fc.target.owner == "Owner" assert fc.target.editor_group == "editorgroup" assert fc.target.editor == "associate" assert fc.target.application_status == "reapplication" # because it hasn't been finalised yet assert fc.target.suggester, 'Suggester not set' assert fc.target.suggester['name'] == fc.form.contact_name.data assert fc.target.suggester['email'] == fc.form.contact_email.data # now do finalise (which will also re-run all of the steps above) fc.finalise() assert fc.target.application_status == "submitted"
def test_02_conditional_disabled(self): s = models.Suggestion(**deepcopy(REAPPLICATION_SOURCE)) # source only, all fields disabled fc = formcontext.ApplicationFormFactory.get_form_context( role="publisher", source=s) assert "pissn" in fc.renderer.disabled_fields assert "eissn" in fc.renderer.disabled_fields assert "contact_name" in fc.renderer.disabled_fields assert "contact_email" in fc.renderer.disabled_fields assert "confirm_contact_email" in fc.renderer.disabled_fields # should validate assert fc.validate() # source only, no contact details, so those fields not disabled del s.data["admin"]["contact"] fc = formcontext.ApplicationFormFactory.get_form_context( role="publisher", source=s) assert "pissn" in fc.renderer.disabled_fields assert "eissn" in fc.renderer.disabled_fields assert "contact_name" not in fc.renderer.disabled_fields assert "contact_email" not in fc.renderer.disabled_fields assert "confirm_contact_email" not in fc.renderer.disabled_fields # should fail to validate assert not fc.validate() # source + form data, everything complete from source, so all fields disabled s = models.Suggestion(**deepcopy(REAPPLICATION_SOURCE)) fd = MultiDict(deepcopy(REAPPLICATION_FORM)) fc = formcontext.ApplicationFormFactory.get_form_context( role="publisher", source=s, form_data=fd) assert "pissn" in fc.renderer.disabled_fields assert "eissn" in fc.renderer.disabled_fields assert "contact_name" in fc.renderer.disabled_fields assert "contact_email" in fc.renderer.disabled_fields assert "confirm_contact_email" in fc.renderer.disabled_fields # should validate assert fc.validate() # source + form data, both source and form missing some values, but disabled fields only draw from source del s.data["admin"]["contact"] rf = deepcopy(REAPPLICATION_FORM) rf["contact_name"] = "Contact Name" fc = formcontext.ApplicationFormFactory.get_form_context( role="publisher", source=s, form_data=fd) assert "pissn" in fc.renderer.disabled_fields assert "eissn" in fc.renderer.disabled_fields assert "contact_name" not in fc.renderer.disabled_fields assert "contact_email" not in fc.renderer.disabled_fields assert "confirm_contact_email" not in fc.renderer.disabled_fields # should fail to validate assert not fc.validate()
def test_03_workflow_editor_notifications(self): ctx = self._make_and_push_test_context() emails = {} # When we make the application unchanged for a short period of time, we don't tell the editors [APPLICATION_SOURCE_1, APPLICATION_SOURCE_2, APPLICATION_SOURCE_3 ] = ApplicationFixtureFactory.make_many_application_sources(count=3) comfortably_idle = app.config['ASSOC_ED_IDLE_DAYS'] + 1 APPLICATION_SOURCE_1['last_manual_update'] = datetime.utcnow( ) - timedelta(days=comfortably_idle) application1 = models.Suggestion(**APPLICATION_SOURCE_1) application1.save() # This exceeds the idle limit, editors should be notified. APPLICATION_SOURCE_2['admin'][ 'application_status'] = constants.APPLICATION_STATUS_IN_PROGRESS extremely_idle = app.config['ED_IDLE_WEEKS'] + 1 APPLICATION_SOURCE_2['last_manual_update'] = datetime.utcnow( ) - timedelta(weeks=extremely_idle) application2 = models.Suggestion(**APPLICATION_SOURCE_2) application2.save() # This one is assigned to the group but not an associate - editors are reminded. extremely_idle = app.config['ED_IDLE_WEEKS'] + 1 APPLICATION_SOURCE_2['admin'][ 'application_status'] = constants.APPLICATION_STATUS_UPDATE_REQUEST APPLICATION_SOURCE_3['last_manual_update'] = datetime.utcnow( ) - timedelta(days=extremely_idle) APPLICATION_SOURCE_3['admin']['editor'] = None application3 = models.Suggestion(**APPLICATION_SOURCE_3) application3.save() models.Suggestion.blockall([ (application1.id, application1.last_updated), (application2.id, application2.last_updated), (application3.id, application3.last_updated) ]) async_workflow_notifications.editor_notifications(emails) assert len(emails) > 0 assert EDITOR_SOURCE['email'] in list(emails.keys()) email_text_catted = " ".join(emails[EDITOR_SOURCE['email']][1]) assert '1 application(s) currently assigned to your Editor Group, "editorgroup", which have no Associate Editor' in email_text_catted assert "1 application(s) which have been assigned to an Associate Editor but have been idle" in email_text_catted ctx.pop()
def test_28_make_provenance(self): acc = models.Account() acc.set_id("test") acc.add_role("associate_editor") acc.add_role("editor") obj1 = models.Suggestion() obj1.set_id("obj1") models.Provenance.make(acc, "act1", obj1) time.sleep(2) prov = models.Provenance.get_latest_by_resource_id("obj1") assert prov.type == "suggestion" assert prov.user == "test" assert prov.roles == ["associate_editor", "editor"] assert len(prov.editor_group) == 0 assert prov.subtype is None assert prov.action == "act1" assert prov.resource_id == "obj1" eg1 = models.EditorGroup() eg1.set_id("associate") eg1.add_associate(acc.id) eg1.save() eg2 = models.EditorGroup() eg2.set_id("editor") eg2.set_editor(acc.id) eg2.save() time.sleep(2) obj2 = models.Suggestion() obj2.set_id("obj2") models.Provenance.make(acc, "act2", obj2, "sub") time.sleep(2) prov = models.Provenance.get_latest_by_resource_id("obj2") assert prov.type == "suggestion" assert prov.user == "test" assert prov.roles == ["associate_editor", "editor"] assert prov.editor_group == ["editor", "associate"] assert prov.subtype == "sub" assert prov.action == "act2" assert prov.resource_id == "obj2"
def test_01_maned_review_success(self): """Give the editor's reapplication form a full workout""" # we start by constructing it from source fc = formcontext.ApplicationFormFactory.get_form_context( role="admin", source=models.Suggestion(**APPLICATION_SOURCE)) assert isinstance(fc, formcontext.ManEdApplicationReview) assert fc.form is not None assert fc.source is not None assert fc.form_data is None assert fc.template is not None # no need to check form rendering - there are no disabled fields # now construct it from form data (with a known source) fc = formcontext.ApplicationFormFactory.get_form_context( role="admin", form_data=MultiDict(APPLICATION_FORM), source=models.Suggestion(**APPLICATION_SOURCE)) assert isinstance(fc, formcontext.ManEdApplicationReview) assert fc.form is not None assert fc.source is not None assert fc.form_data is not None # test each of the workflow components individually ... # pre-validate and ensure that the disabled fields get re-set fc.pre_validate() # no disabled fields, so just test the function runs # run the validation itself fc.form.subject.choices = mock_lcc_choices # set the choices allowed for the subject manually (part of the test) assert fc.validate(), fc.form.errors # run the crosswalk (no need to look in detail, xwalks are tested elsewhere) fc.form2target() assert fc.target is not None # patch the target with data from the source fc.patch_target() assert fc.target.created_date == "2000-01-01T00:00:00Z" assert fc.target.id == "abcdefghijk" # everything else is overridden by the form, so no need to check it has patched # now do finalise (which will also re-run all of the steps above) fc.finalise() assert True # gives us a place to drop a break point later if we need it
def test_02_application(self): forminfo = suggestion_form.SuggestionFormXWalk.obj2form( models.Suggestion(**APPLICATION_SOURCE)) #diff_dicts(APPLICATION_FORMINFO, forminfo) assert forminfo == APPLICATION_FORMINFO form = forms.ManEdApplicationReviewForm( formdata=MultiDict(APPLICATION_FORM)) obj = suggestion_form.SuggestionFormXWalk.form2obj(form) onotes = obj["admin"]["notes"] del obj["admin"]["notes"] cnotes = APPLICATION_SOURCE["admin"]["notes"] csource = deepcopy(APPLICATION_SOURCE) del csource["admin"]["notes"] otext = [n.get("note") for n in onotes] ctext = [n.get("note") for n in cnotes] assert otext == ctext # get rid of the id and created_date in the ready-made fixture application for comparison # the model object is not going to have an id or created_date since it's not been saved yet del csource['id'] del csource['created_date'] del csource["admin"]["current_journal"] del csource["admin"]["related_journal"] #diff_dicts(csource, obj, 'csource', 'modelobj') #diff_dicts(csource["bibjson"], obj["bibjson"]) assert obj == csource
def test_06_make_journal(self): s = models.Suggestion(**ApplicationFixtureFactory.make_application_source()) j = s.make_journal() assert j.id != s.id assert "suggestion" not in j.data assert j.data.get("bibjson", {}).get("active")
def test_01_maned_review_success(self): """Give the editor's application form a full workout""" acc = models.Account() acc.set_id("richard") acc.add_role("admin") ctx = self._make_and_push_test_context(acc=acc) # we start by constructing it from source fc = formcontext.ApplicationFormFactory.get_form_context(role="admin", source=models.Suggestion(**APPLICATION_SOURCE)) assert isinstance(fc, formcontext.ManEdApplicationReview) assert fc.form is not None assert fc.source is not None assert fc.form_data is None assert fc.template is not None # no need to check form rendering - there are no disabled fields # now construct it from form data (with a known source) fc = formcontext.ApplicationFormFactory.get_form_context( role="admin", form_data=MultiDict(APPLICATION_FORM) , source=models.Suggestion(**APPLICATION_SOURCE)) assert isinstance(fc, formcontext.ManEdApplicationReview) assert fc.form is not None assert fc.source is not None assert fc.form_data is not None # test each of the workflow components individually ... # pre-validate and ensure that the disabled fields get re-set fc.pre_validate() # no disabled fields, so just test the function runs # run the validation itself fc.form.subject.choices = mock_lcc_choices # set the choices allowed for the subject manually (part of the test) assert fc.validate(), fc.form.errors # run the crosswalk (no need to look in detail, xwalks are tested elsewhere) fc.form2target() assert fc.target is not None # patch the target with data from the source fc.patch_target() assert fc.target.created_date == "2000-01-01T00:00:00Z" assert fc.target.id == "abcdefghijk" assert fc.target.current_journal == "123456789987654321" assert fc.target.related_journal == "987654321123456789" # everything else is overridden by the form, so no need to check it has patched # now do finalise (which will also re-run all of the steps above) fc.finalise() time.sleep(2) # now check that a provenance record was recorded prov = models.Provenance.get_latest_by_resource_id(fc.target.id) assert prov is not None ctx.pop()
def test_05_maned_review_accept(self): """Give the editor's application form a full workout""" acc = models.Account() acc.set_id("richard") acc.add_role("admin") ctx = self._make_and_push_test_context(acc=acc) # construct a context from a form submission source = deepcopy(APPLICATION_FORM) source["application_status"] = constants.APPLICATION_STATUS_ACCEPTED fd = MultiDict(source) fc = formcontext.ApplicationFormFactory.get_form_context( role="admin", form_data=fd, source=models.Suggestion(**APPLICATION_SOURCE)) fc.finalise() time.sleep(2) # now check that a provenance record was recorded count = 0 for prov in models.Provenance.iterall(): if prov.action == "edit": count += 1 if prov.action == "status:accepted": count += 10 assert count == 11 ctx.pop()
def list_by_title(input, out): # first read in and prep the search data terms = _prep_search_terms(input) conn = esprit.raw.make_connection(None, app.config["ELASTIC_SEARCH_HOST"], None, app.config["ELASTIC_SEARCH_DB"]) application_rows = [] for a in esprit.tasks.scroll(conn, models.Suggestion.__type__, q=APPS_NOT_ACCEPTED, page_size=1000, keepalive='5m'): application = models.Suggestion(_source=a) title = application.bibjson().title alt = application.bibjson().alternative_title titles = [] if title is not None and title != "": titles.append(_normalise_title(title)) if alt is not None and alt != "": titles.append(_normalise_title(alt)) for variants in terms: for v in variants: if v in titles or len([t for t in titles if v in t]) > 0: row = [variants[-1]] + _extract_report_row(application) application_rows.append(row) break journal_rows = [] for j in esprit.tasks.scroll(conn, models.Journal.__type__, q=ALL_JOURNALS, page_size=1000, keepalive='5m'): journal = models.Journal(_source=j) title = journal.bibjson().title alt = journal.bibjson().alternative_title titles = [] if title is not None and title != "": titles.append(_normalise_title(title)) if alt is not None and alt != "": titles.append(_normalise_title(alt)) for variants in terms: for v in variants: if v in titles or len([t for t in titles if v in t]) > 0: row = [variants[-1]] + _extract_journal_row(journal) application_rows.append(row) break with open(out, "w", encoding="utf-8") as o: writer = csv.writer(o) writer.writerow([ "Supplied Title", "Type", "ISSN(s)", "Date Created", "Last Updated", "Date Applied", "Status", "Most Recent Note", "Title Matched", "Alternative Title Matched", "Link" ]) for row in application_rows: writer.writerow(row) for row in journal_rows: writer.writerow(row)
def setUp(self): super(TestTaskSuggestionBulkEdit, self).setUp() acc = models.Account() acc.set_id("0987654321") acc.set_email("*****@*****.**") acc.save() egs = EditorGroupFixtureFactory.make_editor_group_source( "1234567890", "0987654321") egm = models.EditorGroup(**egs) egm.save(blocking=True) self.suggestions = [] for app_src in ApplicationFixtureFactory.make_many_application_sources( count=TEST_SUGGESTION_COUNT): self.suggestions.append(models.Suggestion(**app_src)) self.suggestions[-1].set_editor_group("1234567890") self.suggestions[-1].set_editor("0987654321") self.suggestions[-1].save() self.default_eg = EditorGroupFixtureFactory.setup_editor_group_with_editors( ) self.forbidden_accounts = [ AccountFixtureFactory.make_editor_source()['id'], AccountFixtureFactory.make_assed1_source()['id'], AccountFixtureFactory.make_assed2_source()['id'], AccountFixtureFactory.make_assed3_source()['id'] ] self._make_and_push_test_context(acc=models.Account( **AccountFixtureFactory.make_managing_editor_source()))
def test_04_suggestion_model_rw(self): """Read and write properties into the suggestion model""" s = models.Suggestion() s.set_current_journal("9876543") s.set_related_journal("123456789") s.set_bulk_upload_id("abcdef") s.set_application_status(constants.APPLICATION_STATUS_REJECTED) s.suggested_on = "2001-01-01T00:00:00Z" s.set_articles_last_year(12, "http://aly.com") s.article_metadata = True s.set_suggester("test", "*****@*****.**") assert s.data.get("admin", {}).get("current_journal") == "9876543" assert s.current_journal == "9876543" assert s.related_journal == "123456789" assert s.bulk_upload_id == "abcdef" assert s.application_status == constants.APPLICATION_STATUS_REJECTED assert s.suggested_on == "2001-01-01T00:00:00Z" assert s.articles_last_year.get("count") == 12 assert s.articles_last_year.get("url") == "http://aly.com" assert s.article_metadata is True assert s.suggester.get("name") == "test" assert s.suggester.get("email") == "*****@*****.**" # check over ordered note reading s.add_note("another note", "2010-01-01T00:00:00Z") s.add_note("an old note", "2001-01-01T00:00:00Z") ons = s.ordered_notes assert len(ons) == 2 assert ons[1]["note"] == "an old note" assert ons[0]["note"] == "another note" s.prep() assert 'index' in s, s assert 'application_type' in s['index'], s['index'] assert s['index'][ 'application_type'] == constants.APPLICATION_TYPE_UPDATE_REQUEST s.remove_current_journal() assert s.current_journal is None s.prep() assert 'index' in s, s assert 'application_type' in s['index'], s['index'] assert s['index'][ 'application_type'] == constants.APPLICATION_TYPE_FINISHED s.set_application_status(constants.APPLICATION_STATUS_PENDING) s.prep() assert s['index'][ 'application_type'] == constants.APPLICATION_TYPE_NEW_APPLICATION s.save() s.remove_current_journal() s.remove_related_journal() assert s.current_journal is None assert s.related_journal is None
def test_02_workflow_managing_editor_notifications(self): ctx = self._make_and_push_test_context() emails = {} # When we make the application unchanged for a short period of time, we don't tell the managing editors [APPLICATION_SOURCE_1, APPLICATION_SOURCE_2, APPLICATION_SOURCE_3 ] = ApplicationFixtureFactory.make_many_application_sources(count=3) comfortably_idle = app.config['ASSOC_ED_IDLE_DAYS'] + 1 APPLICATION_SOURCE_1['last_manual_update'] = datetime.utcnow( ) - timedelta(days=comfortably_idle) application1 = models.Suggestion(**APPLICATION_SOURCE_1) application1.save() # This exceeds the idle limit, managing editors should be notified. APPLICATION_SOURCE_2['admin'][ 'application_status'] = constants.APPLICATION_STATUS_IN_PROGRESS extremely_idle = app.config['MAN_ED_IDLE_WEEKS'] + 1 APPLICATION_SOURCE_2['last_manual_update'] = datetime.utcnow( ) - timedelta(weeks=extremely_idle) application2 = models.Suggestion(**APPLICATION_SOURCE_2) application2.save() # This one is ready - managing editors are told as it's now their responsibility. APPLICATION_SOURCE_3['last_manual_update'] = datetime.utcnow() APPLICATION_SOURCE_3['admin'][ 'application_status'] = constants.APPLICATION_STATUS_READY application3 = models.Suggestion(**APPLICATION_SOURCE_3) application3.save() models.Suggestion.blockall([ (application1.id, application1.last_updated), (application2.id, application2.last_updated), (application3.id, application3.last_updated) ]) async_workflow_notifications.managing_editor_notifications(emails) assert len(emails) > 0 assert app.config['MANAGING_EDITOR_EMAIL'] in list(emails.keys()) email_text_catted = " ".join( emails[app.config['MANAGING_EDITOR_EMAIL']][1]) assert '1 application(s) are assigned to an Associate Editor' in email_text_catted assert "There are 1 records in status 'Ready'" in email_text_catted ctx.pop()
def anonymise_suggestion(record): try: sug = models.Suggestion(**record) except DataStructureException: return record sug = _anonymise_admin(sug) sug.set_suggester(anon_name(), anon_email(sug.suggester['email'])) return sug.data
def test_03_suggestion_model_rw(self): """Read and write properties into the suggestion model""" s = models.Suggestion() s.set_current_journal("9876543") s.set_bulk_upload_id("abcdef") assert s.data.get("admin", {}).get("current_journal") == "9876543" assert s.current_journal == "9876543" assert s.bulk_upload_id == "abcdef"
def test_04_workflow_associate_editor_notifications(self): ctx = self._make_and_push_test_context() APPLICATION_SOURCE['last_manual_update'] = datetime.utcnow() application = models.Suggestion(**APPLICATION_SOURCE) application.save(blocking=True) # This application is assigned to associate editor 1, but it is not yet stale enough to require a reminder emails = {} async_workflow_notifications.associate_editor_notifications(emails) assert_false(emails) # When we make the application unchanged for a period of time, we expect a message to be generated [APPLICATION_SOURCE_2, APPLICATION_SOURCE_3 ] = ApplicationFixtureFactory.make_many_application_sources(count=2) comfortably_idle = app.config['ASSOC_ED_IDLE_DAYS'] + 1 APPLICATION_SOURCE_2['last_manual_update'] = datetime.utcnow( ) - timedelta(days=comfortably_idle) application2 = models.Suggestion(**APPLICATION_SOURCE_2) application2.save() extremely_idle = app.config['ASSOC_ED_IDLE_WEEKS'] + 1 APPLICATION_SOURCE_3['last_manual_update'] = datetime.utcnow( ) - timedelta(weeks=extremely_idle) application3 = models.Suggestion(**APPLICATION_SOURCE_3) application3.save() models.Suggestion.blockall([ (application.id, application.last_updated), (application2.id, application2.last_updated), (application3.id, application3.last_updated) ]) async_workflow_notifications.associate_editor_notifications(emails) assert len(emails) > 0 assert ASSED1_SOURCE['email'] in list(emails.keys()) email_text = emails[ASSED1_SOURCE['email']][1].pop() assert 'You have 2 application(s) assigned to you' in email_text assert 'including 1 which have been unchanged' in email_text ctx.pop()
def setUp(self): super(TestTick, self).setUp() self.j_correct = models.Journal(created_date="2014-06-28T11:26:42Z") self.j_correct.set_in_doaj(True) self.j_correct.save() self.j_not_in_doaj_excplicit = models.Journal( created_date="2014-06-28T11:26:42Z") self.j_not_in_doaj_excplicit.set_in_doaj(False) self.j_not_in_doaj_excplicit.save() self.j_not_in_doaj_implicit = models.Journal( created_date="2014-06-28T11:26:42Z") self.j_not_in_doaj_implicit.save() self.j_too_old = models.Journal(created_date="2012-06-28T11:26:42Z") self.j_too_old.set_in_doaj(True) self.j_too_old.save() self.j_too_old_not_in = models.Journal( created_date="2012-06-28T11:26:42Z") self.j_too_old_not_in.set_in_doaj(False) self.j_too_old_not_in.save() self.sugg = models.Suggestion(created_date="2014-06-28T11:26:42Z") self.sugg.save() # update request tests self.j_old_cd_old_reapp = models.Journal( created_date="2012-06-28T11:26:42Z") self.j_old_cd_old_reapp.add_related_application( "123456789", date_accepted="2012-07-28T11:26:42Z") self.j_old_cd_old_reapp.set_in_doaj(True) self.j_old_cd_old_reapp.save() self.j_old_cd_new_reapp = models.Journal( created_date="2012-06-28T11:26:42Z") self.j_old_cd_new_reapp.add_related_application( "123456789", date_accepted="2015-01-01T11:26:42Z") self.j_old_cd_new_reapp.set_in_doaj(True) self.j_old_cd_new_reapp.save() self.j_old_cd_new_reapp_out = models.Journal( created_date="2012-06-28T11:26:42Z") self.j_old_cd_new_reapp_out.add_related_application( "123456789", date_accepted="2015-01-01T11:26:42Z") self.j_old_cd_new_reapp_out.set_in_doaj(False) self.j_old_cd_new_reapp_out.save() # Refresh the type to force changes in the index, then wait for it to be done models.Journal.refresh() models.Suggestion.refresh() time.sleep(2)
def test_06_formcontext_factory(self): fc = formcontext.ApplicationFormFactory.get_form_context() assert isinstance(fc, formcontext.PublicApplication) assert fc.form is not None assert fc.form_data is None assert fc.source is None fc = formcontext.ApplicationFormFactory.get_form_context( role="admin", source=models.Suggestion(**APPLICATION_SOURCE)) assert isinstance(fc, formcontext.ManEdApplicationReview) assert fc.form is not None assert fc.form_data is None assert fc.source is not None fc = formcontext.ApplicationFormFactory.get_form_context( role="editor", source=models.Suggestion(**APPLICATION_SOURCE), form_data=MultiDict(APPLICATION_FORM)) assert isinstance(fc, formcontext.EditorApplicationReview) assert fc.form is not None assert fc.form_data is not None assert fc.source is not None fc = formcontext.ApplicationFormFactory.get_form_context( "associate_editor", source=models.Suggestion(**APPLICATION_SOURCE), form_data=MultiDict(APPLICATION_FORM)) assert isinstance(fc, formcontext.AssEdApplicationReview) assert fc.form is not None assert fc.form_data is not None assert fc.source is not None fc = formcontext.ApplicationFormFactory.get_form_context( "publisher", source=models.Suggestion(**APPLICATION_SOURCE), form_data=MultiDict(APPLICATION_FORM)) assert isinstance(fc, formcontext.PublisherUpdateRequest) assert fc.form is not None assert fc.form_data is not None assert fc.source is not None
def test_02_update_request(self): acc = models.Account() acc.set_id("richard") acc.add_role("admin") ctx = self._make_and_push_test_context(acc=acc) # There needs to be an existing journal in the index for this test to work jsource = JournalFixtureFactory.make_journal_source() del jsource["admin"]["related_applications"] extant_j = models.Journal(**jsource) assert extant_j.last_update_request is None extant_j_created_date = extant_j.created_date extant_j.save() time.sleep(1) # We've added one journal, so there'll be one snapshot already assert models.Journal.count() == 1 h = self.list_today_journal_history_files() assert len(h) == 1 # set up an application which is an update on an existing journal s = models.Suggestion(**APPLICATION_SOURCE) s.set_current_journal("abcdefghijk_journal") s.set_application_status(constants.APPLICATION_STATUS_UPDATE_REQUEST) # set up the form which "accepts" this update request fd = deepcopy(APPLICATION_FORM) fd["application_status"] = constants.APPLICATION_STATUS_ACCEPTED fd = MultiDict(fd) # create and finalise the form context fc = formcontext.ApplicationFormFactory.get_form_context(role="admin", form_data=fd, source=s) # with app.test_request_context(): fc.finalise() # let the index catch up time.sleep(1) j = models.Journal.pull("abcdefghijk_journal") assert j is not None assert j.created_date == extant_j_created_date assert j.last_update_request is not None assert models.Journal.count() == 1 h = self.list_today_journal_history_files() assert h is not None assert len(h) == 2 ctx.pop()
def test_08_sync_owners(self): # suggestion with no current_journal s = models.Suggestion( **ApplicationFixtureFactory.make_application_source()) s.save() models.Suggestion.refresh() s = models.Suggestion.pull(s.id) assert s is not None # journal with no current_application j = models.Journal(**JournalFixtureFactory.make_journal_source()) j.save() models.Journal.refresh() j = models.Journal.pull(j.id) assert j is not None # suggestion with erroneous current_journal s.set_current_journal("asdklfjsadjhflasdfoasf") s.save() models.Suggestion.refresh() s = models.Suggestion.pull(s.id) assert s is not None # journal with erroneous current_application j.set_current_application("kjwfuiwqhu220952gw") j.save() models.Journal.refresh() j = models.Journal.pull(j.id) assert j is not None # suggestion with journal s.set_owner("my_new_owner") s.set_current_journal(j.id) s.save() models.Journal.refresh() j = models.Journal.pull(j.id) assert j.owner == "my_new_owner" # journal with suggestion j.set_owner("another_new_owner") j.set_current_application(s.id) j.save() models.Suggestion.refresh() s = models.Suggestion.pull(s.id) assert s.owner == "another_new_owner"
def test_32_application_all_by_related_journal(self): j = models.Journal() j.set_id(j.makeid()) app1 = models.Suggestion( **ApplicationFixtureFactory.make_application_source()) app1.set_id(app1.makeid()) app1.set_related_journal(j.id) app1.set_created("1970-01-01T00:00:00Z") app1.save() app2 = models.Suggestion( **ApplicationFixtureFactory.make_application_source()) app2.set_id(app2.makeid()) app2.set_related_journal(j.id) app2.set_created("1971-01-01T00:00:00Z") app2.save(blocking=True) # check that we find all the applications when we search, and that they're in the right order all = models.Suggestion.find_all_by_related_journal(j.id) assert len(all) == 2 assert all[0].id == app1.id assert all[1].id == app2.id
def test_03_api_401(self): # make a user account for the authorisation test a1 = models.Account.make_account(username="******", name="a1_name", email="*****@*****.**", roles=["user", "api"], associated_journal_ids=[]) a1_key = a1.api_key a1.save() # a1 has api access # populate the index with an application owned by this owner a = models.Suggestion() a.set_owner("a1_user") bj = a.bibjson() bj.title = "Test Suggestion Title" bj.add_identifier(bj.P_ISSN, "0000-0000") bj.publisher = "Test Publisher" bj.add_url("http://homepage.com", "homepage") a.save() time.sleep(1) with self.app_test.test_client() as t_client: # a successful authenticated query, giving the right result response = t_client.get( '/api/v1/search/applications/issn%3A0000-0000?api_key=' + a1_key) assert response.status_code == 200 # check we got a result by looking for something that must be in the results assert "Test Suggestion Title" in response.data # and a successful but empty search for an application that doesn't belong to them a.set_owner('not_a1_user') a.save() time.sleep(1) response = t_client.get( '/api/v1/search/applications/issn%3A0000-0000?api_key=' + a1_key) assert response.status_code == 200 assert "Test Suggestion Title" not in response.data # and we expect a json 401 error if we fail to authenticate spurious_key = 'blahblahblah' response = t_client.get( '/api/v1/search/applications/issn%3A0000-0000?api_key=' + spurious_key) assert response.status_code == 401 assert response.mimetype == 'application/json' assert "An API Key is required to access this." in response.data
def test_06_retrieve_application_success(self): # set up all the bits we need data = ApplicationFixtureFactory.make_update_request_source() ap = models.Suggestion(**data) ap.save() time.sleep(2) account = models.Account() account.set_id(ap.owner) account.set_name("Tester") account.set_email("*****@*****.**") # call retrieve on the object a = ApplicationsCrudApi.retrieve(ap.id, account) # check that we got back the object we expected assert isinstance(a, OutgoingApplication) assert a.id == ap.id
def test_05_editor_revert_to_in_progress(self): """ Check that editors are permitted to revert applications from 'completed' to 'in progress' """ acc = models.Account() acc.set_id("contextuser") acc.add_role("editor") ctx = self._make_and_push_test_context(acc=acc) # There should be an associate editor to receive an email when the status is changed associate_account = models.Account( **AccountFixtureFactory.make_assed1_source()) associate_account.save(blocking=True) # Check that an editor can change from 'completed' to 'in progress' after a failed review completed_source = APPLICATION_SOURCE.copy() completed_source['admin'][ 'application_status'] = constants.APPLICATION_STATUS_COMPLETED in_progress_form = APPLICATION_FORM.copy() in_progress_form[ 'application_status'] = constants.APPLICATION_STATUS_IN_PROGRESS # Construct the formcontext from form data (with a known source) fc = formcontext.ApplicationFormFactory.get_form_context( role="editor", form_data=MultiDict(in_progress_form), source=models.Suggestion(**completed_source)) assert isinstance(fc, formcontext.EditorApplicationReview) assert fc.form is not None assert fc.source is not None assert fc.form_data is not None # Finalise the formcontext. This should raise an exception because the application has already been accepted. fc.finalise() time.sleep(1.5) # now check that a provenance record was recorded prov = models.Provenance.get_latest_by_resource_id(fc.target.id) assert prov is not None ctx.pop()
def test_03_associate_review_complete(self): """Give the editor's application form a full workout""" acc = models.Account() acc.set_id("contextuser") acc.add_role("associate_editor") ctx = self._make_and_push_test_context(acc=acc) editor = models.Account() editor.set_id("editor") editor.set_email("*****@*****.**") editor.save() eg = models.EditorGroup() eg.set_name(APPLICATION_SOURCE["admin"]["editor_group"]) eg.set_editor("editor") eg.add_associate("contextuser") eg.save() time.sleep(2) # construct a context from a form submission source = deepcopy(APPLICATION_FORM) source["application_status"] = constants.APPLICATION_STATUS_COMPLETED fd = MultiDict(source) fc = formcontext.ApplicationFormFactory.get_form_context( role="associate_editor", form_data=fd, source=models.Suggestion(**APPLICATION_SOURCE)) fc.finalise() time.sleep(2) # now check that a provenance record was recorded count = 0 for prov in models.Provenance.iterall(): if prov.action == "edit": count += 1 if prov.action == "status:completed": count += 10 assert count == 11 ctx.pop()
def test_05_outgoing_application_do(self): # make a blank one oa = OutgoingApplication() # make one from an incoming application model fixture data = ApplicationFixtureFactory.make_update_request_source() ap = models.Suggestion(**data) oa = OutgoingApplication.from_model(ap) # check that it does not contain information that it shouldn't assert oa.data.get("index") is None assert oa.data.get("history") is None assert oa.data.get("admin", {}).get("notes") is None assert oa.data.get("admin", {}).get("editor_group") is None assert oa.data.get("admin", {}).get("editor") is None assert oa.data.get("admin", {}).get("seal") is None assert oa.data.get("admin", {}).get("related_journal") is None # check that it does contain admin information that it should assert oa.data.get("admin", {}).get("current_journal") is not None