def test_article_publish_v2_forced(self): "an unpublished v2 article can be successfully published again, if forced" # ingest and publish the v1 _, _, av = ajson_ingestor.ingest(self.ajson) ajson_ingestor.publish(self.msid, self.version) av = self.freshen(av) self.assertTrue(av.published()) # modify and ingest+publish a v2 self.ajson['article']['version'] = 2 _, _, av2 = ajson_ingestor.ingest_publish(self.ajson) av2 = self.freshen(av2) self.assertTrue(av2.published()) # the v2 should have been published normally. self.assertEqual(utils.ymd(datetime.now()), utils.ymd(av2.datetime_published)) # give the article-json a 'versionDate' - this won't ordinarily happen until further down the line # but lets embed this logic while it's still fresh in everybody's heads. # modify the versionDate of the v2 and ingest+publish again yesterday = datetime.now() - timedelta(days=1) self.ajson['article']['versionDate'] = yesterday _, _, av2v2 = ajson_ingestor.ingest_publish(self.ajson, force=True) av2v2 = self.freshen(av2v2) self.assertEqual(utils.ymd(yesterday), utils.ymd(av2v2.datetime_published))
def test_article_publish_v2_forced2(self): "a PUBLISHED v2 article can be successfully published (again), if forced" av = ajson_ingestor.ingest(self.ajson) ajson_ingestor.publish(self.msid, self.version) av = self.freshen(av) self.assertTrue(av.published()) # modify and ingest+publish a v2 self.ajson['article']['version'] = 2 # there is a versionDate here, but because we're not forcing it, it doesn't get looked for # lax is the distributor of non-v1 pub dates. this may find their way into xml later, but # they will always come from lax. av2 = ajson_ingestor.ingest_publish(self.ajson) av2 = self.freshen(av2) self.assertTrue(av2.published()) self.assertEqual(utils.ymd(datetime.now()), utils.ymd(av2.datetime_published)) # don't set a versionDate, just force a publish # we expect the v2.datetime_publish to remain unchanged del self.ajson['article'][ 'versionDate'] # remember, this was copied from a v1 that had a versionDate! av2v2 = ajson_ingestor.ingest_publish(self.ajson, force=True) av2v2 = self.freshen(av2v2) self.assertEqual(av2.datetime_published, av2v2.datetime_published)
def test_article_publish_v2_forced(self): "an unpublished v2 article can be successfully published again, if forced" # ingest and publish the v1 av = ajson_ingestor.ingest(self.ajson) ajson_ingestor.publish(self.msid, self.version) av = self.freshen(av) self.assertTrue(av.published()) # modify and ingest+publish a v2 self.ajson['article']['version'] = 2 av2 = ajson_ingestor.ingest_publish(self.ajson) av2 = self.freshen(av2) self.assertTrue(av2.published()) # the v2 should have been published normally. self.assertEqual(utils.ymd(datetime.now()), utils.ymd(av2.datetime_published)) # give the article-json a 'versionDate' - this won't ordinarily happen until further down the line # but lets embed this logic while it's still fresh in everybody's heads. # modify the versionDate of the v2 and ingest+publish again yesterday = datetime.now() - timedelta(days=1) self.ajson['article']['versionDate'] = yesterday av2v2 = ajson_ingestor.ingest_publish(self.ajson, force=True) av2v2 = self.freshen(av2v2) self.assertEqual(utils.ymd(yesterday), utils.ymd(av2v2.datetime_published))
def test_out_of_sequence_publish_fails(self): "attempting to ingest an article with a version greater than another *published* version fails" # ingest and publish a v1 _, _, av = ajson_ingestor.ingest(self.ajson) # v1 ajson_ingestor.publish(self.msid, self.version) # now attempt to ingest a v3 self.ajson['article']['version'] = 3 self.assertRaises(StateError, ajson_ingestor.ingest, self.ajson) self.assertEqual(models.Article.objects.count(), 1) self.assertEqual(models.ArticleVersion.objects.count(), 1) av = self.freshen(av) self.assertEqual(av.version, 1) # assert the version hasn't changed
def test_out_of_sequence_publish_fails(self): "attempting to ingest an article with a version greater than another *published* version fails" # ingest and publish a v1 av = ajson_ingestor.ingest(self.ajson) # v1 ajson_ingestor.publish(self.msid, self.version) # now attempt to ingest a v3 self.ajson['article']['version'] = 3 self.assertRaises(StateError, ajson_ingestor.ingest, self.ajson) self.assertEqual(models.Article.objects.count(), 1) self.assertEqual(models.ArticleVersion.objects.count(), 1) av = self.freshen(av) self.assertEqual(av.version, 1) # assert the version hasn't changed
def test_article_publish_v1(self): "an unpublished v1 article can be successfully published" av = ajson_ingestor.ingest(self.ajson) self.assertEqual(models.Journal.objects.count(), 1) self.assertEqual(models.Article.objects.count(), 1) self.assertEqual(models.ArticleVersion.objects.count(), 1) self.assertFalse(av.published()) # publish av = ajson_ingestor.publish(self.msid, self.version) # aaand just make sure we still have the expected number of objects self.assertEqual(models.Journal.objects.count(), 1) self.assertEqual(models.Article.objects.count(), 1) self.assertEqual(models.ArticleVersion.objects.count(), 1) self.assertTrue(av.published()) self.assertTrue(isinstance(av.datetime_published, datetime)) # the pubdate of an unpublished v1 article is the same as that found in the # given json. av = self.freshen(av) expected_pubdate = utils.ymd( utils.todt(self.ajson['article']['published'])) self.assertEqual(expected_pubdate, utils.ymd(av.datetime_published))
def test_article_publish_fails_if_already_published(self): "a published article CANNOT be published again" _, _, av = ajson_ingestor.ingest(self.ajson) av = ajson_ingestor.publish(self.msid, self.version) av = self.freshen(av) self.assertTrue(av.published()) # publish again self.assertRaises(StateError, ajson_ingestor.publish, self.msid, self.version)
def test_publish_dry_run(self): "specifying a dry run does not commit changes to database" _, _, saved_av = ajson_ingestor.ingest(self.ajson) # do an actual ingest first unsaved_av = ajson_ingestor.publish(self.msid, self.version, dry_run=True) self.assertEqual(models.ArticleVersion.objects.count(), 1) # ensure the article version stored has no published date models.ArticleVersion.objects.get(pk=saved_av.pk, datetime_published=None) # and that the object returned *does* have a datetime published self.assertTrue(unsaved_av.published())
def test_article_publish_fails_if_already_published(self): "a published article CANNOT be published again" av = ajson_ingestor.ingest(self.ajson) av = ajson_ingestor.publish(self.msid, self.version) av = self.freshen(av) self.assertTrue(av.published()) # publish again self.assertRaises(StateError, ajson_ingestor.publish, self.msid, self.version)
def test_article_publish_succeeds_for_published_article_if_forced(self): "publication of an already published article can occur only if forced" _, _, av = ajson_ingestor.ingest(self.ajson) av = ajson_ingestor.publish(self.msid, self.version) av = self.freshen(av) expected_pubdate = utils.ymd(utils.todt(self.ajson['article']['published'])) self.assertEqual(expected_pubdate, utils.ymd(av.datetime_published)) # publish again, no changes to pubdate expected av = ajson_ingestor.publish(self.msid, self.version, force=True) av = self.freshen(av) self.assertEqual(expected_pubdate, utils.ymd(av.datetime_published)) # ingest new pubdate, force publication new_pubdate = utils.todt('2016-01-01') self.ajson['article']['published'] = new_pubdate ajson_ingestor.ingest_publish(self.ajson, force=True) av = self.freshen(av) self.assertEqual(utils.ymd(new_pubdate), utils.ymd(av.datetime_published))
def test_article_publish_v2(self): "an unpublished v2 article can be successfully published" _, _, av = ajson_ingestor.ingest(self.ajson) self.assertEqual(models.Journal.objects.count(), 1) self.assertEqual(models.Article.objects.count(), 1) self.assertEqual(models.ArticleVersion.objects.count(), 1) self.assertFalse(av.published()) ajson_ingestor.publish(self.msid, self.version) av = self.freshen(av) self.assertTrue(av.published()) # modify to a v2 and publish self.ajson['article']['version'] = 2 _, _, av2 = ajson_ingestor.ingest_publish(self.ajson) av2 = self.freshen(av2) self.assertEqual(models.ArticleVersion.objects.count(), 2) self.assertTrue(av2.published()) self.assertEqual(utils.ymd(datetime.now()), utils.ymd(av2.datetime_published))
def test_article_publish_v2(self): "an unpublished v2 article can be successfully published" av = ajson_ingestor.ingest(self.ajson) self.assertEqual(models.Journal.objects.count(), 1) self.assertEqual(models.Article.objects.count(), 1) self.assertEqual(models.ArticleVersion.objects.count(), 1) self.assertFalse(av.published()) ajson_ingestor.publish(self.msid, self.version) av = self.freshen(av) self.assertTrue(av.published()) # modify to a v2 and publish self.ajson['article']['version'] = 2 av2 = ajson_ingestor.ingest_publish(self.ajson) av2 = self.freshen(av2) self.assertEqual(models.ArticleVersion.objects.count(), 2) self.assertTrue(av2.published()) self.assertEqual(utils.ymd(datetime.now()), utils.ymd(av2.datetime_published))
def test_article_publish_succeeds_for_published_article_if_forced(self): "publication of an already published article can occur only if forced" av = ajson_ingestor.ingest(self.ajson) av = ajson_ingestor.publish(self.msid, self.version) av = self.freshen(av) expected_pubdate = utils.ymd( utils.todt(self.ajson['article']['published'])) self.assertEqual(expected_pubdate, utils.ymd(av.datetime_published)) # publish again, no changes to pubdate expected av = ajson_ingestor.publish(self.msid, self.version, force=True) av = self.freshen(av) self.assertEqual(expected_pubdate, utils.ymd(av.datetime_published)) # ingest new pubdate, force publication new_pubdate = utils.todt('2016-01-01') self.ajson['article']['published'] = new_pubdate ajson_ingestor.ingest_publish(self.ajson, force=True) av = self.freshen(av) self.assertEqual(utils.ymd(new_pubdate), utils.ymd(av.datetime_published))
def test_publish_dry_run(self): "specifying a dry run does not commit changes to database" saved_av = ajson_ingestor.ingest( self.ajson) # do an actual ingest first unsaved_av = ajson_ingestor.publish(self.msid, self.version, dry_run=True) self.assertEqual(models.ArticleVersion.objects.count(), 1) # ensure the article version stored has no published date models.ArticleVersion.objects.get(pk=saved_av.pk, datetime_published=None) # and that the object returned *does* have a datetime published self.assertTrue(unsaved_av.published())
def handle_single(print_queue, action, infile, msid, version, force, dry_run): data = None log_context = {"msid": msid, "version": version} LOG.info("attempting to %s article %s", action, msid, extra=log_context) # read and check the article-json given, if necessary try: if action in [INGEST, BOTH]: raw_data = infile.read() log_context["data"] = str(raw_data[:25]) + "... (truncated)" if raw_data else "" data = json.loads(raw_data) # vagary of the CLI interface: article id and version are required # these may not match the data given data_version = data["article"].get("version") if not data_version == version: raise StateError( "version in the data (%s) does not match version passed to script (%s)" % (data_version, version) ) data_msid = int(data["article"]["id"]) if not data_msid == msid: raise StateError( "manuscript-id in the data (%s) does not match id passed to script (%s)" % (data_msid, msid) ) except StateError as err: error(print_queue, INVALID, err.message, log_context) except ValueError as err: msg = "could not decode the json you gave me: %r for data: %r" % (err.message, raw_data) error(print_queue, INVALID, msg, log_context) choices = { # all these return a models.ArticleVersion object INGEST: lambda msid, ver, force, data, dry: ajson_ingestor.ingest(data, force, dry_run=dry)[-1], PUBLISH: lambda msid, ver, force, data, dry: ajson_ingestor.publish(msid, ver, force, dry_run=dry), BOTH: lambda msid, ver, force, data, dry: ajson_ingestor.ingest_publish(data, force, dry_run=dry)[-1], } try: av = choices[action](msid, version, force, data, dry_run) success(print_queue, action, av, log_context, dry_run) except StateError as err: error(print_queue, INVALID, "failed to call action %r: %s" % (action, err.message), log_context) except Exception as err: msg = "unhandled exception attempting to %r article: %s" % (action, err) LOG.exception(msg, extra=log_context) error(print_queue, ERROR, msg, log_context)
def test_article_publish_v2_forced2(self): "a PUBLISHED v2 article can be successfully published (again), if forced" _, _, av = ajson_ingestor.ingest(self.ajson) ajson_ingestor.publish(self.msid, self.version) av = self.freshen(av) self.assertTrue(av.published()) # modify and ingest+publish a v2 self.ajson['article']['version'] = 2 # there is a versionDate here, but because we're not forcing it, it doesn't get looked for # lax is the distributor of non-v1 pub dates. this may find their way into xml later, but # they will always come from lax. _, _, av2 = ajson_ingestor.ingest_publish(self.ajson) av2 = self.freshen(av2) self.assertTrue(av2.published()) self.assertEqual(utils.ymd(datetime.now()), utils.ymd(av2.datetime_published)) # don't set a versionDate, just force a publish # we expect the v2.datetime_publish to remain unchanged del self.ajson['article']['versionDate'] # remember, this was copied from a v1 that had a versionDate! _, _, av2v2 = ajson_ingestor.ingest_publish(self.ajson, force=True) av2v2 = self.freshen(av2v2) self.assertEqual(av2.datetime_published, av2v2.datetime_published)
def test_article_publish_v1(self): "an unpublished v1 article can be successfully published" _, _, av = ajson_ingestor.ingest(self.ajson) self.assertEqual(models.Journal.objects.count(), 1) self.assertEqual(models.Article.objects.count(), 1) self.assertEqual(models.ArticleVersion.objects.count(), 1) self.assertFalse(av.published()) # publish av = ajson_ingestor.publish(self.msid, self.version) # aaand just make sure we still have the expected number of objects self.assertEqual(models.Journal.objects.count(), 1) self.assertEqual(models.Article.objects.count(), 1) self.assertEqual(models.ArticleVersion.objects.count(), 1) self.assertTrue(av.published()) self.assertTrue(isinstance(av.datetime_published, datetime)) # the pubdate of an unpublished v1 article is the same as that found in the # given json. av = self.freshen(av) expected_pubdate = utils.ymd(utils.todt(self.ajson['article']['published'])) self.assertEqual(expected_pubdate, utils.ymd(av.datetime_published))
def handle_single(print_queue, action, infile, msid, version, force, dry_run): data = None log_context = {'msid': msid, 'version': version} LOG.info('attempting to %s article %s v%s', action, msid, version, extra=log_context) # read and check the article-json given, if necessary try: if action not in [PUBLISH]: raw_data = infile.read() log_context['data'] = str( raw_data[:25]) + "... (truncated)" if raw_data else '' try: data = json.loads(raw_data) except ValueError as err: msg = "could not decode the json you gave me: %r for data: %r" % ( err.msg, raw_data) raise StateError(codes.BAD_REQUEST, msg) # vagary of the CLI interface: article id and version are required # these may not match the data given data_version = data['article'].get('version') if not data_version == version: raise StateError( codes.BAD_REQUEST, "'version' in the data (%s) does not match 'version' passed to script (%s)" % (data_version, version)) data_msid = int(data['article']['id']) if not data_msid == msid: raise StateError( codes.BAD_REQUEST, "'id' in the data (%s) does not match 'msid' passed to script (%s)" % (data_msid, msid)) except KeyboardInterrupt as err: LOG.warn("ctrl-c caught during data load") raise except StateError as err: error_from_err(print_queue, INVALID, err, force, dry_run, log_context) except BaseException as err: LOG.exception("unhandled exception attempting to ingest article-json", extra=log_context) error(print_queue, ERROR, codes.UNKNOWN, str(err), force, dry_run, log_context, trace=ftb(err)) choices = { # all these return a models.ArticleVersion object INGEST: lambda msid, ver, force, data, dry: ajson_ingestor.ingest( data, force, dry_run=dry), PUBLISH: lambda msid, ver, force, data, dry: ajson_ingestor.publish( msid, ver, force, dry_run=dry), INGEST_PUBLISH: lambda msid, ver, force, data, dry: ajson_ingestor.ingest_publish( data, force, dry_run=dry), } try: av = choices[action](msid, version, force, data, dry_run) success(print_queue, action, av, force, dry_run, log_context) except KeyboardInterrupt as err: LOG.warn("ctrl-c caught during ingest/publish") raise except SystemExit: # `success` and `error` use `exit` to indicate success or failure with their return code. # this is handled in the `job` function, so re-raise it here and handle it there raise except StateError as err: # handled error error_from_err(print_queue, INVALID, err, force, dry_run, log_context) except BaseException as err: # unhandled error msg = "unhandled exception attempting to %r article: %r" % (action, err) LOG.exception(msg, extra=log_context) error(print_queue, ERROR, codes.UNKNOWN, msg, force, dry_run, log_context, trace=ftb(err))