__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" __license__ = "AGPL v3" import factory import pytz import random from comments.factories import SubmissionCommentFactory from scipost.constants import SCIPOST_SUBJECT_AREAS from scipost.models import Contributor from journals.models import Journal from journals.constants import SCIPOST_JOURNALS_DOMAINS from common.helpers import random_scipost_report_doi_label from .constants import ( STATUS_UNASSIGNED, STATUS_EIC_ASSIGNED, STATUS_INCOMING, STATUS_PUBLISHED, SUBMISSION_TYPE, STATUS_RESUBMITTED, STATUS_VETTED, REFEREE_QUALIFICATION, RANKING_CHOICES, QUALITY_SPEC, REPORT_REC, REPORT_STATUSES, STATUS_UNVETTED, STATUS_DRAFT) from .models import Submission, Report, RefereeInvitation, EICRecommendation, EditorialAssignment from faker import Faker class SubmissionFactory(factory.django.DjangoModelFactory): """ Generate random basic Submission instances. """ author_list = factory.Faker('name') submitted_by = factory.Iterator(Contributor.objects.all()) submission_type = factory.Iterator(SUBMISSION_TYPE, getter=lambda c: c[0]) submitted_to = factory.Iterator(Journal.objects.all()) title = factory.Faker('sentence') abstract = factory.Faker('paragraph', nb_sentences=10) list_of_changes = factory.Faker('paragraph', nb_sentences=10) subject_area = factory.Iterator(SCIPOST_SUBJECT_AREAS[0][1], getter=lambda c: c[0]) domain = factory.Iterator(SCIPOST_JOURNALS_DOMAINS, getter=lambda c: c[0]) abstract = factory.Faker('paragraph') author_comments = factory.Faker('paragraph') remarks_for_editors = factory.Faker('paragraph') thread_hash = factory.Faker('uuid4') is_current = True submission_date = factory.Faker('date_this_decade') latest_activity = factory.LazyAttribute(lambda o: Faker().date_time_between( start_date=o.submission_date, end_date="now", tzinfo=pytz.UTC)) preprint = factory.SubFactory('preprints.factories.PreprintFactory') class Meta: model = Submission @classmethod def create(cls, **kwargs): if Contributor.objects.count() < 5: from scipost.factories import ContributorFactory ContributorFactory.create_batch(5) if Journal.objects.count() < 3: from journals.factories import JournalFactory JournalFactory.create_batch(3) return super().create(**kwargs) @factory.post_generation def contributors(self, create, extracted, **kwargs): contribs = Contributor.objects.all() if self.editor_in_charge: contribs = contribs.exclude(id=self.editor_in_charge.id) contribs = contribs.order_by('?')[:random.randint(1, 6)] # Auto-add the submitter as an author self.submitted_by = contribs[0] self.author_list = ', '.join([ '%s %s' % (c.user.first_name, c.user.last_name) for c in contribs]) if not create: return # Add three random authors self.authors.add(*contribs) class UnassignedSubmissionFactory(SubmissionFactory): """ A new incoming Submission without any EIC assigned. """ status = STATUS_UNASSIGNED class EICassignedSubmissionFactory(SubmissionFactory): """ A Submission with an EIC assigned, visible in the pool and refereeing in process. """ status = STATUS_EIC_ASSIGNED open_for_commenting = True open_for_reporting = True @factory.lazy_attribute def editor_in_charge(self): return Contributor.objects.order_by('?').first() @factory.post_generation def eic_assignment(self, create, extracted, **kwargs): if create: EditorialAssignmentFactory(submission=self, to=self.editor_in_charge) @factory.post_generation def referee_invites(self, create, extracted, **kwargs): for i in range(random.randint(1, 3)): RefereeInvitationFactory(submission=self) for i in range(random.randint(0, 2)): AcceptedRefereeInvitationFactory(submission=self) for i in range(random.randint(0, 2)): FulfilledRefereeInvitationFactory(submission=self) @factory.post_generation def comments(self, create, extracted, **kwargs): if create: for i in range(random.randint(0, 3)): SubmissionCommentFactory(content_object=self) @factory.post_generation def eic_recommendation(self, create, extracted, **kwargs): if create: EICRecommendationFactory(submission=self) class ResubmittedSubmissionFactory(EICassignedSubmissionFactory): """ A Submission that has a newer Submission version in the database with a successive version number. """ status = STATUS_RESUBMITTED open_for_commenting = False open_for_reporting = False is_current = False @factory.post_generation def successive_submission(self, create, extracted, **kwargs): """ Generate a second Submission that's the successive version of the resubmitted Submission """ if create and extracted is not False: # Prevent infinite loops by checking the extracted argument ResubmissionFactory(preprint__identifier_wo_vn_nr=self.preprint.identifier_wo_vn_nr, previous_submission=False) @factory.post_generation def gather_successor_data(self, create, extracted, **kwargs): """ Gather some data from Submission with same arxiv id such that this Submission more or less looks like any regular real resubmission. """ submission = Submission.objects.filter( preprint__identifier_wo_vn_nr=self.preprint.identifier_wo_vn_nr).exclude( preprint__vn_nr=self.preprint.vn_nr).first() if not submission: return self.author_list = submission.author_list self.submitted_by = submission.submitted_by self.editor_in_charge = submission.editor_in_charge self.submission_type = submission.submission_type self.submitted_to = submission.submitted_to self.title = submission.title self.subject_area = submission.subject_area self.domain = submission.domain self.title = submission.title self.authors.set(self.authors.all()) @factory.post_generation def referee_invites(self, create, extracted, **kwargs): """ This Submission is deactivated for refereeing. """ for i in range(random.randint(0, 2)): FulfilledRefereeInvitationFactory(submission=self) for i in range(random.randint(1, 3)): CancelledRefereeInvitationFactory(submission=self) class ResubmissionFactory(EICassignedSubmissionFactory): """ This Submission is a newer version of a Submission which is already known by the SciPost database. """ status = STATUS_INCOMING open_for_commenting = True open_for_reporting = True vn_nr = 2 @factory.post_generation def previous_submission(self, create, extracted, **kwargs): if create and extracted is not False: # Prevent infinite loops by checking the extracted argument ResubmittedSubmissionFactory( preprint__identifier_wo_vn_nr=self.preprint.identifier_wo_vn_nr, successive_submission=False) @factory.post_generation def gather_predecessor_data(self, create, extracted, **kwargs): """ Gather some data from Submission with same arxiv id such that this Submission more or less looks like any regular real resubmission. """ submission = Submission.objects.filter( identifier_wo_vn_nr=self.preprint.identifier_wo_vn_nr).exclude( preprint__vn_nr=self.preprint.vn_nr).first() if not submission: return self.author_list = submission.author_list self.submitted_by = submission.submitted_by self.editor_in_charge = submission.editor_in_charge self.submission_type = submission.submission_type self.submitted_to = submission.submitted_to self.title = submission.title self.subject_area = submission.subject_area self.domain = submission.domain self.title = submission.title self.authors.set(self.authors.all()) @factory.post_generation def referee_invites(self, create, extracted, **kwargs): """ Referees for resubmissions are invited once the cycle has been chosen. """ pass class PublishedSubmissionFactory(EICassignedSubmissionFactory): status = STATUS_PUBLISHED open_for_commenting = False open_for_reporting = False @factory.post_generation def generate_publication(self, create, extracted, **kwargs): if create and extracted is not False: from journals.factories import PublicationFactory PublicationFactory( journal=self.submitted_to.doi_label, accepted_submission=self, title=self.title, author_list=self.author_list) @factory.post_generation def eic_assignment(self, create, extracted, **kwargs): if create: EditorialAssignmentFactory(submission=self, to=self.editor_in_charge, completed=True) @factory.post_generation def referee_invites(self, create, extracted, **kwargs): for i in range(random.randint(2, 4)): FulfilledRefereeInvitationFactory(submission=self) for i in range(random.randint(0, 2)): CancelledRefereeInvitationFactory(submission=self) class ReportFactory(factory.django.DjangoModelFactory): status = factory.Iterator(REPORT_STATUSES, getter=lambda c: c[0]) submission = factory.Iterator(Submission.objects.all()) report_nr = 1 date_submitted = factory.Faker('date_time_this_decade') vetted_by = factory.Iterator(Contributor.objects.all()) author = factory.Iterator(Contributor.objects.all()) strengths = factory.Faker('paragraph') weaknesses = factory.Faker('paragraph') report = factory.Faker('paragraph') requested_changes = factory.Faker('paragraph') qualification = factory.Iterator(REFEREE_QUALIFICATION[1:], getter=lambda c: c[0]) validity = factory.Iterator(RANKING_CHOICES[1:], getter=lambda c: c[0]) significance = factory.Iterator(RANKING_CHOICES[1:], getter=lambda c: c[0]) originality = factory.Iterator(RANKING_CHOICES[1:], getter=lambda c: c[0]) clarity = factory.Iterator(RANKING_CHOICES[1:], getter=lambda c: c[0]) formatting = factory.Iterator(QUALITY_SPEC[1:], getter=lambda c: c[0]) grammar = factory.Iterator(QUALITY_SPEC[1:], getter=lambda c: c[0]) recommendation = factory.Iterator(REPORT_REC[1:], getter=lambda c: c[0]) remarks_for_editors = factory.Faker('paragraph') flagged = factory.Faker('boolean', chance_of_getting_true=10) anonymous = factory.Faker('boolean', chance_of_getting_true=75) class Meta: model = Report class DraftReportFactory(ReportFactory): status = STATUS_DRAFT vetted_by = None class UnVettedReportFactory(ReportFactory): status = STATUS_UNVETTED vetted_by = None class VettedReportFactory(ReportFactory): status = STATUS_VETTED needs_doi = True doideposit_needs_updating = factory.Faker('boolean') doi_label = factory.lazy_attribute(lambda n: random_scipost_report_doi_label()) pdf_report = factory.Faker('file_name', extension='pdf') class RefereeInvitationFactory(factory.django.DjangoModelFactory): submission = factory.SubFactory('submissions.factories.SubmissionFactory') referee = factory.lazy_attribute(lambda o: Contributor.objects.exclude( id__in=o.submission.authors.all()).order_by('?').first()) title = factory.lazy_attribute(lambda o: o.referee.title) first_name = factory.lazy_attribute(lambda o: o.referee.user.first_name) last_name = factory.lazy_attribute(lambda o: o.referee.user.last_name) email_address = factory.lazy_attribute(lambda o: o.referee.user.email) date_invited = factory.lazy_attribute(lambda o: o.submission.latest_activity) nr_reminders = factory.lazy_attribute(lambda o: random.randint(0, 4)) date_last_reminded = factory.lazy_attribute(lambda o: o.submission.latest_activity) invitation_key = factory.Faker('md5') invited_by = factory.lazy_attribute(lambda o: o.submission.editor_in_charge) class Meta: model = RefereeInvitation class AcceptedRefereeInvitationFactory(RefereeInvitationFactory): accepted = True date_responded = factory.lazy_attribute(lambda o: Faker().date_time_between( start_date=o.date_invited, end_date="now", tzinfo=pytz.UTC)) @factory.post_generation def report(self, create, extracted, **kwargs): if create: VettedReportFactory(submission=self.submission, author=self.referee) class FulfilledRefereeInvitationFactory(AcceptedRefereeInvitationFactory): fulfilled = True date_responded = factory.lazy_attribute(lambda o: Faker().date_time_between( start_date=o.date_invited, end_date="now", tzinfo=pytz.UTC)) @factory.post_generation def report(self, create, extracted, **kwargs): if create: VettedReportFactory(submission=self.submission, author=self.referee) class CancelledRefereeInvitationFactory(AcceptedRefereeInvitationFactory): fulfilled = False cancelled = True date_responded = factory.lazy_attribute(lambda o: Faker().date_time_between( start_date=o.date_invited, end_date="now", tzinfo=pytz.UTC)) class EICRecommendationFactory(factory.django.DjangoModelFactory): submission = factory.Iterator(Submission.objects.all()) date_submitted = factory.lazy_attribute(lambda o: Faker().date_time_between( start_date=o.submission.submission_date, end_date="now", tzinfo=pytz.UTC)) remarks_for_authors = factory.Faker('paragraph') requested_changes = factory.Faker('paragraph') remarks_for_editorial_college = factory.Faker('paragraph') recommendation = factory.Iterator(REPORT_REC[1:], getter=lambda c: c[0]) version = 1 active = True class Meta: model = EICRecommendation class EditorialAssignmentFactory(factory.django.DjangoModelFactory): """ An EditorialAssignmentFactory should always have a `submission` explicitly assigned. This will mostly be done using the post_generation hook in any SubmissionFactory. """ submission = None to = factory.Iterator(Contributor.objects.all()) accepted = True deprecated = False completed = False date_created = factory.lazy_attribute(lambda o: o.submission.latest_activity) date_answered = factory.lazy_attribute(lambda o: o.submission.latest_activity) class Meta: model = EditorialAssignment