arxiv.submission.domain.event package

Data structures for submissions events.

  • Events have unique identifiers generated from their data (creation, agent, submission).

  • Events provide methods to update a submission based on the event data.

  • Events provide validation methods for event data.

Writing new events/commands

Events/commands are implemented as classes that inherit from Event. It should:

  • Be a dataclass (i.e. be decorated with dataclasses.dataclass()).

  • Define (using dataclasses.field()) associated data.

  • Implement a validation method with the signature validate(self, submission: Submission) -> None (see below).

  • Implement a projection method with the signature project(self, submission: Submission) -> Submission: that mutates the passed domain.submission.Submission instance. The projection must not generate side-effects, because it will be called any time we are generating the state of a submission. If you need to generate a side-effect, see Registering event callbacks.

  • Be fully documented. Be sure that the class docstring fully describes the meaning of the event/command, and that both public and private methods have at least a summary docstring.

  • Have a corresponding unittest.TestCase in arxiv.submission.domain.tests.test_events.

Adding validation to events

Each command/event class should implement an instance method validate(self, submission: Submission) -> None that raises InvalidEvent exceptions if the data on the event instance is not valid.

For clarity, it’s a good practice to individuate validation steps as separate private instance methods, and call them from the public validate method. This makes it easier to identify which validation criteria are being applied, in what order, and what those criteria mean.

See SetPrimaryClassification for an example.

We could consider standalone validation functions for validation checks that are performed on several event types (instead of just private instance methods).

Registering event callbacks

The base Event provides support for callbacks that are executed when an event instance is committed. To attach a callback to an event type, use the Event.bind() decorator. For example:

@SetTitle.bind()
def do_this_when_a_title_is_set(event, before, after, agent):
    ...
    return []

Callbacks must have the signature (event: Event, before: Submission, after: Submission, creator: Agent) -> Iterable[Event]. event is the event instance being committed that triggered the callback. before and after are the states of the submission before and after the event was applied, respectively. agent is the agent responsible for any subsequent events created by the callback, and should be used for that purpose.

The callback should not concern itself with persistence; that is handled by Event.commit(). Any mutations of submission should be made by returning the appropriate command/event instances.

The circumstances under which the callback is executed can be controlled by passing a condition callable to the decorator. This should have the signature (event: Event, before: Submission, after: Submission, creator: Agent) -> bool; if it returns True, the callback will be executed. For example:

@SetTitle.bind(condition=lambda e, b, a, c: e.title == 'foo')
def do_this_when_a_title_is_set_to_foo(event, before, after, agent):
    ...
    return []

When do things actually happen?

Callbacks are triggered when the commit() method is called, usually by core.save(). Normally, any event instances returned by the callback are applied and committed right away, in order.

Setting config.ENABLE_CALLBACKS=0 will disable callbacks entirely.

class arxiv.submission.domain.event.AddClassifierResults(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, classifier=<Classifiers.CLASSIC: 'classic'>, results=<factory>)[source]

Bases: arxiv.submission.domain.event.base.Event

Add the results of a classifier to a submission.

NAME = 'add classifer results'
NAMED = 'classifier results added'
classifier = 'classic'
project(submission)[source]

Add the annotation to the submission.

Return type

Submission

validate(submission)[source]

Verify that the classifier is a known value.

Return type

None

class arxiv.submission.domain.event.AddFeature(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, feature_type=<Type.WORD_COUNT: 'words'>, feature_value=0)[source]

Bases: arxiv.submission.domain.event.base.Event

Add feature metadata to a submission.

NAME = 'add feature metadata'
NAMED = 'feature metadata added'
feature_type = 'words'
feature_value = 0
project(submission)[source]

Add the annotation to the submission.

Return type

Submission

validate(submission)[source]

Verify that the feature type is a known value.

Return type

None

class arxiv.submission.domain.event.AddSecondaryClassification(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, category=None)[source]

Bases: arxiv.submission.domain.event.base.Event

Add a secondary Classification to a submission.

NAME = 'add cross-list classification'
NAMED = 'cross-list classification added'
category = None
project(submission)[source]

Add a Classification as a secondary classification.

Return type

Submission

validate(submission)[source]

Validate the secondary classification category to add.

Return type

None

class arxiv.submission.domain.event.Announce(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, arxiv_id=None)[source]

Bases: arxiv.submission.domain.event.base.Event

Announce the current version of the submission.

NAME = 'publish submission'
NAMED = 'submission announced'
arxiv_id = None
project(submission)[source]

Set the arXiv ID on the submission.

Return type

Submission

validate(submission)[source]

Make sure that we have a valid arXiv ID.

Return type

None

class arxiv.submission.domain.event.ConfirmAuthorship(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, submitter_is_author=True)[source]

Bases: arxiv.submission.domain.event.base.Event

The submitting user asserts whether they are an author of the paper.

NAME = 'confirm that submitter is an author'
NAMED = 'submitter authorship status confirmed'
project(submission)[source]

Update the authorship flag on the submission.

Return type

Submission

submitter_is_author = True
validate(submission)[source]

Cannot apply to a finalized submission.

Return type

None

class arxiv.submission.domain.event.ConfirmCompiledPreview(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>)[source]

Bases: arxiv.submission.domain.event.base.Event

Confirm that the submitter successfully compiled a preview.

NAME = 'confirm submission preview is compiled'
NAMED = 'confirmed that submission preview was compiled'
project(submission)[source]

Set Submission.submitter_compiled_preview.

Return type

Submission

validate(submission)[source]
Return type

None

class arxiv.submission.domain.event.ConfirmContactInformation(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>)[source]

Bases: arxiv.submission.domain.event.base.Event

Submitter has verified their contact information.

NAME = 'confirm contact information'
NAMED = 'contact information confirmed'
project(submission)[source]

Update Submission.submitter_contact_verified.

Return type

Submission

validate(submission)[source]

Cannot apply to a finalized submission.

Return type

None

class arxiv.submission.domain.event.ConfirmPolicy(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>)[source]

Bases: arxiv.submission.domain.event.base.Event

The submitting user accepts the arXiv submission policy.

NAME = 'confirm policy acceptance'
NAMED = 'policy acceptance confirmed'
project(submission)[source]

Set the policy flag on the submission.

Return type

Submission

validate(submission)[source]

Cannot apply to a finalized submission.

Return type

None

class arxiv.submission.domain.event.ConfirmPreview(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>)[source]

Bases: arxiv.submission.domain.event.base.Event

Confirm that the paper and abstract previews are acceptable.

NAME = 'approve submission preview'
NAMED = 'submission preview approved'
project(submission)[source]

Set Submission.submitter_confirmed_preview.

Return type

Submission

validate(submission)[source]

Validate data for ConfirmPreview.

Return type

None

class arxiv.submission.domain.event.CreateSubmission(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>)[source]

Bases: arxiv.submission.domain.event.base.Event

Creation of a new domain.submission.Submission.

NAME = 'create submission'
NAMED = 'submission created'
project(submission=None)[source]

Create a new domain.submission.Submission.

Return type

Submission

validate(*args, **kwargs)[source]

Validate creation of a submission.

Return type

None

class arxiv.submission.domain.event.CreateSubmissionVersion(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>)[source]

Bases: arxiv.submission.domain.event.base.Event

Creates a new version of a submission.

Takes the submission back to “working” state; the user or client may make additional changes before finalizing the submission.

NAME = 'create a new version'
NAMED = 'new version created'
project(submission)[source]

Increment the version number, and reset several fields.

Return type

Submission

validate(submission)[source]

Only applies to announced submissions.

Return type

None

class arxiv.submission.domain.event.FinalizeSubmission(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>)[source]

Bases: arxiv.submission.domain.event.base.Event

Send the submission to the queue for announcement.

NAME = 'finalize submission for announcement'
NAMED = 'submission finalized'
REQUIRED = ['creator', 'primary_classification', 'submitter_contact_verified', 'submitter_accepts_policy', 'license', 'source_content', 'metadata']
REQUIRED_METADATA = ['title', 'abstract', 'authors_display']
project(submission)[source]

Set Submission.is_finalized.

Return type

Submission

validate(submission)[source]

Ensure that all required data/steps are complete.

Return type

None

class arxiv.submission.domain.event.Reclassify(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, category=None)[source]

Bases: arxiv.submission.domain.event.base.Event

Reclassify a submission.

NAME = 'reclassify submission'
NAMED = 'submission reclassified'
category = None
project(submission)[source]

Set domain.Submission.primary_classification.

Return type

Submission

validate(submission)[source]

Validate the primary classification category.

Return type

None

class arxiv.submission.domain.event.RemoveSecondaryClassification(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, category=None)[source]

Bases: arxiv.submission.domain.event.base.Event

Remove secondary Classification from submission.

NAME = 'remove cross-list classification'
NAMED = 'cross-list classification removed'
category = None
project(submission)[source]

Remove from Submission.secondary_classification.

Return type

Submission

validate(submission)[source]

Validate the secondary classification category to remove.

Return type

None

class arxiv.submission.domain.event.Rollback(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>)[source]

Bases: arxiv.submission.domain.event.base.Event

Roll back to the most recent announced version, or delete.

NAME = 'roll back or delete'
NAMED = 'rolled back or deleted'
project(submission)[source]

Decrement the version number, and reset fields.

Return type

Submission

validate(submission)[source]

Only applies to submissions in an unannounced state.

Return type

None

class arxiv.submission.domain.event.SetACMClassification(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, acm_class='')[source]

Bases: arxiv.submission.domain.event.base.Event

Update the ACM classification codes of a submission.

MAX_LENGTH = 160
NAME = 'update ACM classification'
NAMED = 'ACM classification updated'
acm_class = ''

E.g. F.2.2; I.2.7

static cleanup(value)[source]

Perform light cleanup.

Return type

str

project(submission)[source]

Update the ACM classification on a domain.submission.Submission.

Return type

Submission

validate(submission)[source]

Validate the ACM classification value.

Return type

None

class arxiv.submission.domain.event.SetAbstract(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, abstract='')[source]

Bases: arxiv.submission.domain.event.base.Event

Update the abstract of a submission.

MAX_LENGTH = 1920
MIN_LENGTH = 20
NAME = 'update abstract'
NAMED = 'abstract updated'
abstract = ''
static cleanup(value)[source]

Perform some light tidying on the abstract.

Return type

str

project(submission)[source]

Update the abstract on a domain.submission.Submission.

Return type

Submission

validate(submission)[source]

Validate the abstract value.

Return type

None

class arxiv.submission.domain.event.SetAuthors(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, authors=<factory>, authors_display=None)[source]

Bases: arxiv.submission.domain.event.base.Event

Update the authors on a domain.submission.Submission.

NAME = 'update authors'
NAMED = 'authors updated'
authors_display = None

The authors string may be provided.

static cleanup(s)[source]

Perform some light tidying on the provided author string(s).

Return type

str

project(submission)[source]

Replace Submission.metadata.authors.

Return type

Submission

validate(submission)[source]

May not apply to a finalized submission.

Return type

None

class arxiv.submission.domain.event.SetComments(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, comments='')[source]

Bases: arxiv.submission.domain.event.base.Event

Update the comments of a submission.

MAX_LENGTH = 400
NAME = 'update comments'
NAMED = 'comments updated'
static cleanup(value)[source]

Light cleanup on comment value.

Return type

str

comments = ''
project(submission)[source]

Update the comments on a domain.submission.Submission.

Return type

Submission

validate(submission)[source]

Validate the comments value.

Return type

None

class arxiv.submission.domain.event.SetDOI(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, doi='')[source]

Bases: arxiv.submission.domain.event.base.Event

Update the external DOI of a submission.

NAME = 'add a DOI'
NAMED = 'DOI added'
static cleanup(value)[source]

Perform some light tidying on the title.

Return type

str

doi = ''
project(submission)[source]

Update the doi on a domain.submission.Submission.

Return type

Submission

validate(submission)[source]

Validate the DOI value.

Return type

None

class arxiv.submission.domain.event.SetJournalReference(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, journal_ref='')[source]

Bases: arxiv.submission.domain.event.base.Event

Update the journal reference of a submission.

NAME = 'add a journal reference'
NAMED = 'journal reference added'
static cleanup(value)[source]

Perform light cleanup.

Return type

str

journal_ref = ''
project(submission)[source]

Update the journal reference on a domain.submission.Submission.

Return type

Submission

validate(submission)[source]

Validate the journal reference value.

Return type

None

class arxiv.submission.domain.event.SetLicense(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, license_name=None, license_uri=None)[source]

Bases: arxiv.submission.domain.event.base.Event

The submitter has selected a license for their submission.

NAME = 'select distribution license'
NAMED = 'distribution license selected'
license_name = None
license_uri = None
project(submission)[source]

Set domain.Submission.license.

Return type

Submission

validate(submission)[source]

Validate the selected license.

Return type

None

class arxiv.submission.domain.event.SetMSCClassification(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, msc_class='')[source]

Bases: arxiv.submission.domain.event.base.Event

Update the MSC classification codes of a submission.

MAX_LENGTH = 160
NAME = 'update MSC classification'
NAMED = 'MSC classification updated'
static cleanup(value)[source]

Perform some light fixes on the MSC classification value.

Return type

str

msc_class = ''
project(submission)[source]

Update the MSC classification on a domain.submission.Submission.

Return type

Submission

validate(submission)[source]

Validate the MSC classification value.

Return type

None

class arxiv.submission.domain.event.SetPrimaryClassification(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, category=None)[source]

Bases: arxiv.submission.domain.event.base.Event

Update the primary classification of a submission.

NAME = 'set primary classification'
NAMED = 'primary classification set'
category = None
project(submission)[source]

Set domain.Submission.primary_classification.

Return type

Submission

validate(submission)[source]

Validate the primary classification category.

Return type

None

class arxiv.submission.domain.event.SetReportNumber(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, report_num='')[source]

Bases: arxiv.submission.domain.event.base.Event

Update the report number of a submission.

NAME = 'update report number'
NAMED = 'report number updated'
static cleanup(value)[source]

Light cleanup on report number value.

Return type

str

project(submission)[source]

Set report number on a domain.submission.Submission.

Return type

Submission

report_num = ''
validate(submission)[source]

Validate the report number value.

Return type

None

class arxiv.submission.domain.event.SetTitle(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, title='')[source]

Bases: arxiv.submission.domain.event.base.Event

Update the title of a submission.

ALLOWED_HTML = ['br', 'sup', 'sub', 'hr', 'em', 'strong', 'h']
MAX_LENGTH = 240
MIN_LENGTH = 5
NAME = 'update title'
NAMED = 'title updated'
static cleanup(value)[source]

Perform some light tidying on the title.

Return type

str

project(submission)[source]

Update the title on a domain.submission.Submission.

Return type

Submission

title = ''
validate(submission)[source]

Validate the title value.

Return type

None

class arxiv.submission.domain.event.SetUploadPackage(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, identifier=<factory>, checksum=<factory>, uncompressed_size=0, compressed_size=0, source_format=<Format.UNKNOWN: None>)[source]

Bases: arxiv.submission.domain.event.base.Event

Set the upload workspace for this submission.

NAME = 'set the upload package'
NAMED = 'upload package set'
compressed_size = 0
project(submission)[source]

Replace SubmissionContent metadata on the submission.

Return type

Submission

source_format = None
uncompressed_size = 0
validate(submission)[source]

Validate data for SetUploadPackage.

Return type

None

class arxiv.submission.domain.event.UnConfirmCompiledPreview(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>)[source]

Bases: arxiv.submission.domain.event.base.Event

Unconfirm that the submitter successfully compiled a preview.

NAME = 'unconfirm submission preview is compiled'
NAMED = 'unconfirmed that submission preview was compiled'
project(submission)[source]

Set Submission.submitter_compiled_preview.

Return type

Submission

validate(submission)[source]
Return type

None

class arxiv.submission.domain.event.UnFinalizeSubmission(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>)[source]

Bases: arxiv.submission.domain.event.base.Event

Withdraw the submission from the queue for announcement.

NAME = 're-open submission for modification'
NAMED = 'submission re-opened for modification'
project(submission)[source]

Set Submission.is_finalized.

Return type

Submission

validate(submission)[source]

Validate the unfinalize action.

Return type

None

class arxiv.submission.domain.event.UnsetUploadPackage(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>)[source]

Bases: arxiv.submission.domain.event.base.Event

Unset the upload workspace for this submission.

NAME = 'unset the upload package'
NAMED = 'upload package unset'
project(submission)[source]

Set Submission.source_content to None.

Return type

Submission

validate(submission)[source]

Validate data for UnsetUploadPackage.

Return type

None

class arxiv.submission.domain.event.UpdateUploadPackage(creator, created=None, proxy=None, client=None, submission_id=None, committed=False, before=None, after=None, event_type=<factory>, event_version=<factory>, checksum=<factory>, uncompressed_size=0, compressed_size=0, source_format=<Format.UNKNOWN: None>)[source]

Bases: arxiv.submission.domain.event.base.Event

Update the upload workspace on this submission.

NAME = 'update the upload package'
NAMED = 'upload package updated'
compressed_size = 0
project(submission)[source]

Replace SubmissionContent metadata on the submission.

Return type

Submission

source_format = None
uncompressed_size = 0
validate(submission)[source]

Validate data for SetUploadPackage.

Return type

None