def test_save_from_scratch_without_creation_event(self, mock_database): """An exception is raised when there is no creation event.""" mock_database.store_events = mock_store_events user = User(12345, '*****@*****.**') e2 = UpdateMetadata(creator=user, metadata=[['title', 'foo']]) with self.assertRaises(NoSuchSubmission): save(e2)
def setUp(self): """An arXiv user is submitting a new paper.""" self.submitter = events.domain.User(1234, email='*****@*****.**', forename='Jane', surname='User') # Create and finalize a new submission. cc0 = 'http://creativecommons.org/publicdomain/zero/1.0/' with self.app.app_context(): classic.create_all() self.submission, _ = events.save( events.CreateSubmission(creator=self.submitter), events.VerifyContactInformation(creator=self.submitter), events.AssertAuthorship( creator=self.submitter, submitter_is_author=True ), events.SelectLicense( creator=self.submitter, license_uri=cc0, license_name='CC0 1.0' ), events.AcceptPolicy(creator=self.submitter), events.SetPrimaryClassification( creator=self.submitter, category='cs.DL' ), events.AttachSourceContent( creator=self.submitter, location="https://submit.arxiv.org/upload/123", checksum="a9s9k342900skks03330029k", format='tex', mime_type="application/zip", identifier=123, size=593992 ), events.UpdateMetadata( creator=self.submitter, metadata=[ ('title', 'Foo title'), ('abstract', "One morning, as Gregor Samsa was..."), ('comments', '5 pages, 2 turtle doves'), ('report_num', 'asdf1234'), ('doi', '10.01234/56789'), ('journal_ref', 'Foo Rev 1, 2 (1903)') ] ), events.UpdateAuthors( creator=self.submitter, authors=[events.Author( order=0, forename='Bob', surname='Paulson', email='*****@*****.**', affiliation='Fight Club' )] ), events.FinalizeSubmission(creator=self.submitter) )
def test_create_and_update_authors(self, mock_database): """Save multiple events for a nonexistant submission.""" mock_database.store_events = mock_store_events user = User(12345, '*****@*****.**') e = CreateSubmission(creator=user) e2 = UpdateAuthors(creator=user, authors=[ Author(0, forename='Joe', surname="Bloggs", email="*****@*****.**") ]) submission, events = save(e, e2) self.assertIsInstance(submission.metadata.authors[0], Author)
def test_save_events_on_existing_submission(self, mock_db): """Save multiple sets of events in separate calls to :func:`.save`.""" cache = defaultdict(list) def mock_store_events_with_cache(*events, submission): if submission.submission_id is None: submission.submission_id = 1 for event in events: event.committed = True event.submission_id = submission.submission_id cache[event.submission_id].append(event) return submission def mock_get_events(submission_id): return cache[submission_id] mock_db.store_events = mock_store_events_with_cache mock_db.get_events = mock_get_events # Here is the first set of events. user = User(12345, '*****@*****.**') e = CreateSubmission(creator=user) e2 = UpdateMetadata(creator=user, metadata=[['title', 'foo']]) submission, _ = save(e, e2) submission_id = submission.submission_id # Now we apply a second set of events. e3 = UpdateMetadata(creator=user, metadata=[['abstract', 'bar']]) submission2, _ = save(e3, submission_id=submission_id) # The submission state reflects all three events. self.assertEqual(submission2.metadata.abstract, 'bar', "State of the submission should reflect both sets" " of events.") self.assertEqual(submission2.metadata.title, 'foo', "State of the submission should reflect both sets" " of events.") self.assertEqual(submission2.created, e.created, "The creation date of the submission should be the" " original creation date.") self.assertEqual(submission2.submission_id, submission_id, "The submission ID should remain the same.")
def test_save_events_from_scratch(self, mock_database): """Save multiple events for a nonexistant submission.""" mock_database.store_events = mock_store_events user = User(12345, '*****@*****.**') e = CreateSubmission(creator=user) e2 = UpdateMetadata(creator=user, metadata=[['title', 'foo']]) submission, events = save(e, e2) self.assertEqual(submission.metadata.title, 'foo') self.assertIsInstance(submission.submission_id, int) self.assertEqual(submission.created, e.created)
def test_save_creation_event(self, mock_database): """A :class:`.CreationEvent` is passed.""" mock_database.store_events = mock_store_events user = User(12345, '*****@*****.**') event = CreateSubmission(creator=user) submission, events = save(event) self.assertIsInstance(submission, Submission, "A submission instance should be returned") self.assertIsInstance(events[0], Event, "Should return a list of events") self.assertEqual(events[0], event, "The first event should be the event that was passed") self.assertIsNotNone(submission.submission_id, "Submission ID should be set.")
def create_submission(data: dict, headers: dict, user_data: dict, client_data: dict, token: str) -> Response: """ Create a new submission. Implements the hook for :meth:`sword.SWORDCollection.add_submission`. Parameters ---------- data : dict Deserialized compact JSON-LD document. headers : dict Request headers from the client. Returns ------- dict Response data. int HTTP status code. dict Headers to add to the response. """ logger.debug('Received request to create submission') user, client, proxy = _get_agents(headers, user_data, client_data) logger.debug(f'User: {user}; client: {client}, proxy: {proxy}') agents = dict(creator=user, client=client, proxy=proxy) create = ev.CreateSubmission(creator=user, client=client, proxy=proxy) events = handlers.handle_submission(data, agents) try: submission, events = ev.save(create, *events) except (ev.InvalidEvent, ev.InvalidStack) as e: raise BadRequest(str(e)) from e except ev.SaveError as e: logger.error('Problem interacting with database: (%s) %s', str(type(e)), str(e)) raise InternalServerError('Problem interacting with database') from e response_headers = { 'Location': url_for('submission.get_submission', submission_id=submission.submission_id) } return submission.to_dict(), status.HTTP_201_CREATED, response_headers
def update_submission(data: dict, headers: dict, user_data: dict, client_data: dict, token: str, submission_id: str) \ -> Response: """Update the submission.""" user, client, proxy = _get_agents(headers, user_data, client_data) agents = dict(creator=user, client=client, proxy=proxy) events = handlers.handle_submission(data, agents) try: submission, events = ev.save(*events, submission_id=submission_id) except ev.NoSuchSubmission as e: raise NotFound(f"No submission found with id {submission_id}") except (ev.InvalidEvent, ev.InvalidStack) as e: raise BadRequest(str(e)) from e except ev.SaveError as e: raise InternalServerError('Problem interacting with database') from e response_headers = { 'Location': url_for('submission.get_submission', creator=user, submission_id=submission.submission_id) } return submission.to_dict(), status.HTTP_200_OK, response_headers
def process_submission(s): """Process a submission from a tsvfile row.""" # TODO: Make sure forename surname separation are better try: forename, surname = s['submitter_name'].rsplit(maxsplit=1) except ValueError: forename = '' surname = s['submitter_name'] submitter = events.domain.User(s['submitter_id'], email=s['submitter_email'], forename=forename, surname=surname) metadata = [('title', s['title']), ('abstract', s['abstract']), ('comments', s['comments']), ('report_num', s['report_num']), ('doi', s['doi']), ('journal_ref', s['journal_ref'])] submission, stack = events.save(events.CreateSubmission(creator=submitter)) if s.get('is_author') == '1': submission, stack = events.save(events.AssertAuthorship( creator=submitter, submitter_is_author=True), submission_id=submission.submission_id) else: submission, stack = events.save(events.AssertAuthorship( creator=submitter, submitter_is_author=False), submission_id=submission.submission_id) if s.get('agree_policy') == '1': submission, stack = events.save(events.AcceptPolicy(creator=submitter), submission_id=submission.submission_id) if s.get('userinfo') == '1': submission, stack = events.save( events.VerifyContactInformation(creator=submitter), submission_id=submission.submission_id) submission, stack = events.save( events.UpdateAuthors(authors_display=s['authors'], creator=submitter), events.UpdateMetadata(creator=submitter, metadata=metadata), events.SetPrimaryClassification(creator=submitter, category=s['category']), submission_id=submission.submission_id) # Parse the license license_uri = s.get('license') if license_uri: submission, stack = events.save(events.SelectLicense( creator=submitter, license_uri=license_uri), submission_id=submission.submission_id) if s.get('package'): submission, stack = events.save(events.AttachSourceContent( location='https://example.arxiv.org/' + s['package'], format=s['source_format'], checksum='0', identifier=1, creator=submitter), submission_id=submission.submission_id) if s.get('status') not in INVALID_STATUSES: submission, stack = events.save( events.FinalizeSubmission(creator=submitter), submission_id=submission.submission_id) return submission.submission_id
def test_classic_workflow(self, submitter=None, metadata=None, authors=None): """Submitter proceeds through workflow in a linear fashion.""" # Instantiate objects that have not yet been instantiated or use defaults. if submitter is None: submitter = self.submitter if metadata is None: metadata = [ ('title', 'Foo title'), ('abstract', "One morning, as Gregor Samsa was waking up..."), ('comments', '5 pages, 2 turtle doves'), ('report_num', 'asdf1234'), ('doi', '10.01234/56789'), ('journal_ref', 'Foo Rev 1, 2 (1903)') ] # TODO: Process data in dictionary form to events.Author objects. if authors is None: authors = [events.Author(order=0, forename='Bob', surname='Paulson', email='*****@*****.**', affiliation='Fight Club' )] with in_memory_db() as session: # Submitter clicks on 'Start new submission' in the user dashboard. submission, stack = events.save( events.CreateSubmission(creator=submitter) ) self.assertIsNotNone(submission.submission_id, "A submission ID is assigned") self.assertEqual(len(stack), 1, "A single command is executed.") db_submission = session.query(classic.models.Submission)\ .get(submission.submission_id) self.assertEqual(db_submission.submission_id, submission.submission_id, "A row is added to the submission table") self.assertEqual(db_submission.submitter_id, submitter.native_id, "Submitter ID set on submission") self.assertEqual(db_submission.submitter_email, submitter.email, "Submitter email set on submission") self.assertEqual(db_submission.submitter_name, submitter.name, "Submitter name set on submission") self.assertEqual(db_submission.created, submission.created, "Creation datetime set correctly") # TODO: What else to check here? # /start: Submitter completes the start submission page. license_uri = 'http://creativecommons.org/publicdomain/zero/1.0/' submission, stack = events.save( events.VerifyContactInformation(creator=submitter), events.AssertAuthorship( creator=submitter, submitter_is_author=True ), events.SelectLicense( creator=submitter, license_uri=license_uri, license_name='CC0 1.0' ), events.AcceptPolicy(creator=submitter), events.SetPrimaryClassification( creator=submitter, category='cs.DL' ), submission_id=submission.submission_id ) self.assertEqual(len(stack), 6, "Six commands have been executed in total.") db_submission = session.query(classic.models.Submission)\ .get(submission.submission_id) self.assertEqual(db_submission.userinfo, 1, "Contact verification set correctly in database.") self.assertEqual(db_submission.is_author, 1, "Authorship status set correctly in database.") self.assertEqual(db_submission.license, license_uri, "License set correctly in database.") self.assertEqual(db_submission.agree_policy, 1, "Policy acceptance set correctly in database.") self.assertEqual(len(db_submission.categories), 1, "A single category is associated in the database") self.assertEqual(db_submission.categories[0].is_primary, 1, "Primary category is set correct in the database") self.assertEqual(db_submission.categories[0].category, 'cs.DL', "Primary category is set correct in the database") # /addfiles: Submitter has uploaded files to the file management # service, and verified that they compile. Now they associate the # content package with the submission. submission, stack = events.save( events.AttachSourceContent( creator=submitter, location="https://submit.arxiv.org/upload/123", checksum="a9s9k342900skks03330029k", format='tex', mime_type="application/zip", identifier=123, size=593992 ), submission_id=submission.submission_id ) self.assertEqual(len(stack), 7, "Seven commands have been executed in total.") db_submission = session.query(classic.models.Submission)\ .get(submission.submission_id) self.assertEqual(db_submission.must_process, 0, "Processing status is set correctly in database") self.assertEqual(db_submission.source_size, 593992, "Source package size set correctly in database") self.assertEqual(db_submission.source_format, 'tex', "Source format set correctly in database") # /metadata: Submitter adds metadata to their submission, including # authors. In this package, we model authors in more detail than # in the classic system, but we should preserve the canonical # format in the db for legacy components' sake. submission, stack = events.save( events.UpdateMetadata( creator=submitter, metadata=metadata ), events.UpdateAuthors( creator=submitter, authors=authors ), submission_id=submission.submission_id ) db_submission = session.query(classic.models.Submission)\ .get(submission.submission_id) self.assertEqual(db_submission.title, dict(metadata)['title'], "Title updated as expected in database") self.assertEqual(db_submission.abstract, dict(metadata)['abstract'], "Abstract updated as expected in database") self.assertEqual(db_submission.comments, dict(metadata)['comments'], "Comments updated as expected in database") self.assertEqual(db_submission.report_num, dict(metadata)['report_num'], "Report number updated as expected in database") self.assertEqual(db_submission.doi, dict(metadata)['doi'], "DOI updated as expected in database") self.assertEqual(db_submission.journal_ref, dict(metadata)['journal_ref'], "Journal ref updated as expected in database") author_str = ';'.join([f"{author.forename} {author.surname} ({author.affiliation})" for author in authors]) self.assertEqual(db_submission.authors, author_str, "Authors updated in canonical format in database") self.assertEqual(len(stack), 9, "Nine commands have been executed in total.") # /preview: Submitter adds a secondary classification. submission, stack = events.save( events.AddSecondaryClassification( creator=submitter, category='cs.IR' ), submission_id=submission.submission_id ) db_submission = session.query(classic.models.Submission)\ .get(submission.submission_id) self.assertEqual(len(db_submission.categories), 2, "A secondary category is added in the database") secondaries = [ db_cat for db_cat in db_submission.categories if db_cat.is_primary == 0 ] self.assertEqual(len(secondaries), 1, "A secondary category is added in the database") self.assertEqual(secondaries[0].category, 'cs.IR', "A secondary category is added in the database") self.assertEqual(len(stack), 10, "Ten commands have been executed in total.") # /preview: Submitter finalizes submission. finalize = events.FinalizeSubmission(creator=submitter) submission, stack = events.save( finalize, submission_id=submission.submission_id ) db_submission = session.query(classic.models.Submission)\ .get(submission.submission_id) self.assertEqual(db_submission.status, db_submission.SUBMITTED, "Submission status set correctly in database") self.assertEqual(db_submission.submit_time, finalize.created, "Submit time is set.") self.assertEqual(len(stack), 11, "Eleven commands have been executed in total.")