diff --git a/README.md b/README.md
index 0a4608961069118a9a1404f4966efe48fdb0e225..d2a93bd1c4ecda2ef2dfa8ba185d2a3dfa633505 100644
--- a/README.md
+++ b/README.md
@@ -129,6 +129,20 @@ Users of the portal are known as Contributors and are created through the regist
 
 You can create a number of users, and use the admin site to give them various permissions through memberships of certain groups. For example, you'll want members of the SciPost Administrators and Editorial Administrators groups in order to access the internal management and editorial tools.
 
+### Initial data
+If you're working on a (almost) empty database, one can easily fill its test database using one of the built-in commands. To create few instances for each available object, simply run:
+
+```shell
+(scipostenv) $ ./manage.py populate_db --all
+```
+
+Run the help argument to find arguments to create instances for individual models.
+
+```shell
+(scipostenv) $ ./manage.py populate_db --help
+```
+
+
 ## Maintaining database migratons
 Every time fields in any of the models change, a [database migration](https://docs.djangoproject.com/en/1.10/topics/migrations/)
 needs to be created and applied. The first documents a database change and its
diff --git a/commentaries/constants.py b/commentaries/constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..5fe492bf1f29a6f2d63f6d10742f5ee9c8ba05e2
--- /dev/null
+++ b/commentaries/constants.py
@@ -0,0 +1,6 @@
+COMMENTARY_PUBLISHED = 'published'
+COMMENTARY_PREPRINT = 'preprint'
+COMMENTARY_TYPES = (
+    (COMMENTARY_PUBLISHED, 'published paper'),
+    (COMMENTARY_PREPRINT, 'arXiv preprint'),
+)
diff --git a/commentaries/factories.py b/commentaries/factories.py
index 60f1625f2f365bd240db1d1aa7e5d7beb2a9fa73..1f9a3fd2844931cad7b986eb92d695ffd72f46c9 100644
--- a/commentaries/factories.py
+++ b/commentaries/factories.py
@@ -1,42 +1,54 @@
 import factory
 
-from scipost.constants import DISCIPLINE_PHYSICS, SCIPOST_SUBJECT_AREAS
+from scipost.constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS
 from scipost.factories import ContributorFactory
+from scipost.models import Contributor
 from journals.constants import SCIPOST_JOURNALS_DOMAINS
-from common.helpers import random_arxiv_identifier_with_version_number
+from common.helpers import random_arxiv_identifier_with_version_number, random_external_doi
 
-from .models import Commentary, COMMENTARY_TYPES
+from .constants import COMMENTARY_TYPES
+from .models import Commentary
+
+from faker import Faker
 
 
 class CommentaryFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Commentary
-        abstract = True
 
     requested_by = factory.SubFactory(ContributorFactory)
-    vetted_by = factory.SubFactory(ContributorFactory)
-    type = COMMENTARY_TYPES[0][0]
-    discipline = DISCIPLINE_PHYSICS
-    domain = SCIPOST_JOURNALS_DOMAINS[0][0]
-    subject_area = SCIPOST_SUBJECT_AREAS[0][1][0][0]
-    pub_title = factory.Faker('bs')
-    pub_DOI = '10.1103/PhysRevB.92.214427'
+    type = factory.Iterator(COMMENTARY_TYPES, getter=lambda c: c[0])
+    discipline = factory.Iterator(SCIPOST_DISCIPLINES, getter=lambda c: c[0])
+    domain = factory.Iterator(SCIPOST_JOURNALS_DOMAINS, getter=lambda c: c[0])
+    subject_area = factory.Iterator(SCIPOST_SUBJECT_AREAS[0][1], getter=lambda c: c[0])
+    pub_title = factory.Faker('text')
+    pub_DOI = factory.Sequence(lambda n: random_external_doi())
     arxiv_identifier = factory.Sequence(lambda n: random_arxiv_identifier_with_version_number())
-    author_list = factory.Faker('name')
-    pub_abstract = factory.Faker('text')
+    arxiv_link = factory.Faker('uri')
+    pub_abstract = factory.lazy_attribute(lambda x: Faker().paragraph())
+
+    @factory.post_generation
+    def arxiv_link(self, create, extracted, **kwargs):
+        self.arxiv_link = 'https://arxiv.org/abs/%s' % self.arxiv_identifier
+        self.arxiv_or_DOI_string = self.arxiv_identifier
 
     @factory.post_generation
     def create_urls(self, create, extracted, **kwargs):
         self.parse_links_into_urls(commit=create)
 
-
-class EmptyCommentaryFactory(CommentaryFactory):
-    pub_DOI = None
-    arxiv_identifier = None
+    @factory.post_generation
+    def add_authors(self, create, extracted, **kwargs):
+        contributors = list(Contributor.objects.order_by('?')
+                            .exclude(pk=self.requested_by.pk).all()[:4])
+        for contrib in contributors:
+            self.author_list += ', %s %s' % (contrib.user.first_name, contrib.user.last_name)
+            if create:
+                self.authors.add(contrib)
 
 
 class VettedCommentaryFactory(CommentaryFactory):
     vetted = True
+    vetted_by = factory.SubFactory(ContributorFactory)
 
 
 class UnpublishedVettedCommentaryFactory(VettedCommentaryFactory):
diff --git a/commentaries/models.py b/commentaries/models.py
index ac6dab51bf5723cbad95556ed95bfa5c13ec9641..51490c7d8fc3c344e9ed2f3104db5a3cb2b80823 100644
--- a/commentaries/models.py
+++ b/commentaries/models.py
@@ -8,17 +8,10 @@ from scipost.behaviors import ArxivCallable, TimeStampedModel
 from scipost.models import Contributor
 from scipost.constants import SCIPOST_DISCIPLINES, DISCIPLINE_PHYSICS, SCIPOST_SUBJECT_AREAS
 
+from .constants import COMMENTARY_TYPES
 from .managers import CommentaryManager
 
 
-COMMENTARY_PUBLISHED = 'published'
-COMMENTARY_PREPRINT = 'preprint'
-COMMENTARY_TYPES = (
-    (COMMENTARY_PUBLISHED, 'published paper'),
-    (COMMENTARY_PREPRINT, 'arXiv preprint'),
-)
-
-
 class Commentary(ArxivCallable, TimeStampedModel):
     """
     A Commentary contains all the contents of a SciPost Commentary page for a given publication.
diff --git a/comments/factories.py b/comments/factories.py
index 010251601a8ea7186e8df653d3cdef9ff54422ae..de3bf41067008f93d86c590bd583c42884844e56 100644
--- a/comments/factories.py
+++ b/comments/factories.py
@@ -1,16 +1,46 @@
 import factory
+import pytz
 
 from django.utils import timezone
 
+from commentaries.factories import VettedCommentaryFactory
 from scipost.factories import ContributorFactory
+from submissions.factories import EICassignedSubmissionFactory
+from theses.factories import VettedThesisLinkFactory
 
+from .constants import STATUS_VETTED
 from .models import Comment
 
+from faker import Faker
+
+timezone.now()
+
 
 class CommentFactory(factory.django.DjangoModelFactory):
+    author = factory.SubFactory(ContributorFactory)
+    comment_text = factory.lazy_attribute(lambda x: Faker().paragraph())
+    remarks_for_editors = factory.lazy_attribute(lambda x: Faker().paragraph())
+    file_attachment = Faker().file_name(extension='pdf')
+    status = STATUS_VETTED  # All comments will have status vetted!
+    vetted_by = factory.SubFactory(ContributorFactory)
+    date_submitted = Faker().date_time_between(start_date="-3y", end_date="now", tzinfo=pytz.UTC)
+
     class Meta:
         model = Comment
+        abstract = True
 
-    author = factory.SubFactory(ContributorFactory)
-    comment_text = factory.Faker('text')
-    date_submitted = timezone.now()
+
+class CommentaryCommentFactory(CommentFactory):
+    commentary = factory.SubFactory(VettedCommentaryFactory)
+
+
+class SubmissionCommentFactory(CommentFactory):
+    submission = factory.SubFactory(EICassignedSubmissionFactory)
+
+
+class ThesislinkCommentFactory(CommentFactory):
+    thesislink = factory.SubFactory(VettedThesisLinkFactory)
+
+
+class ReplyCommentFactory(CommentFactory):
+    in_reply_to_comment = factory.SubFactory(SubmissionCommentFactory)
diff --git a/common/helpers/__init__.py b/common/helpers/__init__.py
index 78f99d9b8e50de1c0ce2fc15ae82ddf0eb23ef85..abe97df91f6222671cdcf4338f5ac1a39d0073ba 100644
--- a/common/helpers/__init__.py
+++ b/common/helpers/__init__.py
@@ -1,6 +1,8 @@
 import random
 import string
 
+from journals.constants import SCIPOST_JOURNALS_SUBMIT
+
 
 def model_form_data(model, form_class, form_kwargs={}):
     '''
@@ -29,6 +31,7 @@ def model_form_data(model, form_class, form_kwargs={}):
     form_fields = list(form_class(**form_kwargs).fields.keys())
     return filter_keys(model_data, form_fields)
 
+
 def random_arxiv_identifier_with_version_number():
     return random_arxiv_identifier_without_version_number() + "v0"
 
@@ -37,6 +40,34 @@ def random_arxiv_identifier_without_version_number():
     return random_digits(4) + "." + random_digits(5)
 
 
+def random_scipost_journal():
+    return random.choice(SCIPOST_JOURNALS_SUBMIT)[0]
+
+
+def random_external_journal():
+    return random.choice((
+        'PhysRevA.',
+        'PhysRevB.',
+        'PhysRevC.',
+        'nature.'
+        'S0550-3213(01)',
+        '1742-5468/',
+        '0550-3213(96)'
+    ))
+
+
+def random_pub_number():
+    return '%i.%i.%s' % (random.randint(1, 9), random.randint(1, 9), random_digits(3))
+
+
+def random_scipost_doi():
+    return '10.21468/%s.%s' % (random_scipost_journal(), random_pub_number())
+
+
+def random_external_doi():
+    return '10.%s/%s%s' % (random_digits(5), random_external_journal(), random_pub_number())
+
+
 def random_digits(n):
     return "".join(random.choice(string.digits) for _ in range(n))
 
diff --git a/common/helpers/test.py b/common/helpers/test.py
index 2c0d7c96d9724179fc73d98a7a14709ba2d35fbb..e7ed63a924a7a08acbaaba035fd49e9e394d1d3e 100644
--- a/common/helpers/test.py
+++ b/common/helpers/test.py
@@ -1,4 +1,5 @@
 import scipost.management.commands.add_groups_and_permissions
 
+
 def add_groups_and_permissions():
     scipost.management.commands.add_groups_and_permissions.Command().handle(verbose=False)
diff --git a/docs/contributors/editorial_administrators/production.rst b/docs/contributors/editorial_administrators/production.rst
index 1a42a9c8d24605e4f845beed1c29888baeb2a004..89b6d428ee387546e0c10cb564f549839ccdcd32 100644
--- a/docs/contributors/editorial_administrators/production.rst
+++ b/docs/contributors/editorial_administrators/production.rst
@@ -5,7 +5,7 @@ Production of SciPost Publications
 
 This guide is meant for **Editorial Administrators** and **Production Officers**. It describes the post-acceptance workflow from paper acceptance to publication.
 
-Version: 2016-12-20.
+Version: 2017-05-11.
 
 
 Formatting check
@@ -13,8 +13,8 @@ Formatting check
 
    If the Submission's references are not properly formatted,
    and/or if these do not include the required DOIs,
-   the authors should be emailed and asked to do so,
-   by providing either the ``.bbl`` file or the ``.bib`` source.
+   the authors should be emailed and asked to provide them,
+   by sending in either an updated ``.bbl`` file or ``.bib`` source.
 
    Any extra material provided by the authors which supplements
    the arXiv source should be put in a folder ``EXTRA_FROM_AUTH``.
@@ -24,7 +24,7 @@ Source retrieval and folder preparation
 ---------------------------------------
 
    #. On the SciPost server, navigate to folder
-      ``[Journal full name]/IN PRODUCTION``.
+      ``[Journal full name]/IN_PRODUCTION``.
    #. Create a folder
       ``[journal abbrev. name]_[arxiv identifier]_[first author last name]``,
       e.g. ``SciPost_Phys_1604.98141v3_Smart``.
@@ -32,10 +32,13 @@ Source retrieval and folder preparation
    #. NOTE: the file will be named ``####.#####v#``, which is not recognized
       as a ``.tar.gz`` file on a UNIX system. Rename the file
       ``####.####v#.tar.gz``. Unzip it to produce the folder ``####.#####v#``.
+      If this produces another tar file, it is because the submission consists of
+      a single ``.tex`` file; you should then rename this to ``####.#####v#.tex``.
    #. Copy the paper’s sources one level down (so from ``####.#####v#`` to
       the current directory). BE CAREFUL: if the authors have included any of
       the SciPost style files (``SciPost.cls``, ``SciPost_bibstyle.bst``), DO NOT
-      copy those down.
+      copy those down. You can skip this step if the previous step immediately led 
+      to a ``.tex`` file.
    #. Copy the files in
       ``[Journal full name]/v##_production/FILES_TO_COPY_IN_PAPER_DIR``
       to the current directory. There are 5 files:
@@ -44,7 +47,7 @@ Source retrieval and folder preparation
 	 * ``logo_scipost_with_bgd.pdf``
 	 * ``SciPost_bibstyle.bst``
 	 * ``SciPost.cls``
-	 * ``SciPost_Phys_Skeleton.tex``
+	 * ``SciPost_[Phys, or other as appropriate]_Skeleton.tex``
 
    #. Copy the skeleton ``.tex`` source to a new file using the name convention
       ``[Journal abbrev. name]_####_#####v#_[first author last name].tex``
@@ -91,14 +94,14 @@ General LaTeX tips
 Step-by-step procedure
 ~~~~~~~~~~~~~~~~~~~~~~
 
-   #. TODO: PAPER CITATION 1 and 2
+   #. TODO: PAPER CITATION
 
-      In these two places, fill the missing numbers in the citation header::
+      In this place, fill the missing numbers in the citation header::
 
       \rhead{\small \href{https://scipost.org/SciPostPhys.?.?.???}{SciPost Phys. ?, ??? (20??)}}
 
 
-      The first argument of the ``href`` is the simple permanent URL for the publication. This includes 3 numbers: the volume number, issue, and three-digit paper number, for example ``SciPostPhys.1.2.011``. At this stage, leave the paper number to ``???``: this number will be assigned and filled in in the last stage of production.
+      The first argument of the ``href`` is the simple permanent URL for the publication. This includes 3 numbers: the volume number, issue, and three-digit paper number, for example ``SciPostPhys.1.2.011``. Verify the appropriate issue number (this will be verified later by an EdAdmin). At this stage, leave the paper number to ``???``: this number will be assigned and filled in in the last stage of production.
 
       The second argument to the ``href`` uses the simple citation, dropping the issue number, for example ``SciPostPhys. 1, 011 (2016)``.
 
@@ -146,7 +149,7 @@ Step-by-step procedure
 	[title]
 	}}}\end{center}
 
-      Paste the title in this block. If the title is multiline, end it with ``\\``
+      Paste the title in this block. End the title with ``\\``
       in order to ensure proper uniform spacing between the lines.
 
 
@@ -155,7 +158,8 @@ Step-by-step procedure
       Author names should be in format ``firstname lastname``, e.g. ``John Smith``,
       and should be in textbf. No ``comma`` but rather an ``and`` before
       the last author. If multiple authors and affiliations, use ``\textsuperscript``
-      and positive integer labels, with a ``*`` for the corresponding author.
+      and positive integer labels, with a ``$\star$`` for the corresponding author.
+      If multiple email addresses are given, reference them with ``$\dagger$, ...``.
 
 
    #. TODO: AFFILIATIONS
@@ -172,8 +176,8 @@ Step-by-step procedure
 
 	``\href{mailto:[email]}{\small \sf [email]}``
 
-      prepended with ``*`` if corresponding author. If no email is to be given,
-      comment out the ``* \href{mailto:[email]}{\small \sf [email]}`` line.
+      prepended with ``$\star$`` if corresponding author. If no email is to be given,
+      comment out the ``$\star$ \href{mailto:[email]}{\small \sf [email]}`` line.
 
       If a web link is offered, use the format ``{\small \url{[url]}}``.
 
@@ -198,7 +202,7 @@ Step-by-step procedure
 
 	\vspace{10pt}
 	\noindent\rule{\textwidth}{1pt}
-	\tableofcontents\thispagestyle{fancy}
+	\tableofcontents
 	\noindent\rule{\textwidth}{1pt}
 	\vspace{10pt}
 
@@ -206,10 +210,13 @@ Step-by-step procedure
 
 	\vspace{10pt}
 	%\noindent\rule{\textwidth}{1pt}
-	%\tableofcontents\thispagestyle{fancy}
+	%\tableofcontents
 	\noindent\rule{\textwidth}{1pt}
 	\vspace{10pt}
 
+      If a single horizontal line is pushed to the next page, correct by
+      playing with negatime ``\vspace``.
+      
 
    #. TODO: COPYRIGHT
 
@@ -226,7 +233,7 @@ Step-by-step procedure
       format ``DD-MM-YYYY``. Be careful to use the correct submission data,
       namely that of the original submission.
 
-      The published date will be filled in later on.
+      The accepted and published dates will be filled in later on.
 
 
    #. TODO: DOI
@@ -237,13 +244,16 @@ Step-by-step procedure
 
    #. TODO: LINENO
 
-      During proofs stage, activate line numbers.
+      During proofs stage, make sure line numbers are activated (they should
+      be by default).
 
 
    #. TODO: CONTENTS
 
       Paste the entire bulk of the paper in this block,
       including all sections and eventual appendices.
+      Check that there are no appendices after the references in the
+      original tex file.
 
 
    #. TODO: BIBNR
@@ -372,6 +382,8 @@ References formatting
 
       * Volume number is in bold.
 
+      * Issue number can be left out; if included, in parentheses after volume nr.
+
       * Year is in parentheses.
 
       * Commas separate all elements.
@@ -395,6 +407,13 @@ References formatting
       \J. Phys. A: Math. Gen. Afterwards, volume 40 (2007) onwards, it's
       \J. Phys. A: Math. Theor.
 
+      Entries in the bibliography which are not references but footnotes,
+      should be formatted as such in the main text (using ``\footnote{}``).
+
+
+      Check that all DOIs work. Remove the ``\meta`` at the end of the bibitem
+      if it is present.
+    
 
 Layout verification
 ~~~~~~~~~~~~~~~~~~~
@@ -413,7 +432,7 @@ Proofs
      should be copied into new files carrying the ``_proofs_v[nr]`` suffix,
      for example ``SciPost_Phys_1699_9999v9_Bee_proofs_v1.tex``.
 
-   * The ``.pdf`` proofs should be email to the authors for verification.
+   * The ``.pdf`` proofs should be emailed to the authors for verification.
      Authors should return either an annotated pdf or a list of corrections
      by plain text email.
 
@@ -443,7 +462,7 @@ Preparation of final version of record
 
    #. Within this folder, take the author-accepted version tex file and rename it using the convention [journal name abbrev]\_[volume nr]\_[issue nr]\_[paper nr].tex.
 
-   #. In this tex source, replace the ??? with the 3-digit paper number (6 places: 4 in preamble, 2 in copyright statement).
+   #. In this tex source, replace the ??? with the 3-digit paper number (3 places: 2 in preamble, 1 in copyright statement).
 
    #. Ensure that the author names are in format Abe Bee, Cee Dee and Elle Fine.
 
diff --git a/journals/factories.py b/journals/factories.py
new file mode 100644
index 0000000000000000000000000000000000000000..203fe8cb6ca4e14f252ec32dc1d8f9b36660aaba
--- /dev/null
+++ b/journals/factories.py
@@ -0,0 +1,109 @@
+import factory
+import datetime
+import pytz
+
+from django.utils import timezone
+
+from common.helpers import random_digits
+from journals.constants import SCIPOST_JOURNALS
+from submissions.factories import PublishedSubmissionFactory
+
+from .models import Journal, Volume, Issue, Publication
+
+from faker import Faker
+
+
+class JournalFactory(factory.django.DjangoModelFactory):
+    name = factory.Iterator(SCIPOST_JOURNALS, getter=lambda c: c[0])
+    doi_label = factory.Iterator(SCIPOST_JOURNALS, getter=lambda c: c[0])
+    issn = factory.lazy_attribute(lambda n: random_digits(8))
+
+    class Meta:
+        model = Journal
+        django_get_or_create = ('name', 'doi_label',)
+
+
+class VolumeFactory(factory.django.DjangoModelFactory):
+    in_journal = factory.SubFactory(JournalFactory)
+    number = factory.Sequence(lambda n: n + 1)
+    doi_label = factory.Faker('md5')
+
+    @factory.post_generation
+    def doi(self, create, extracted, **kwargs):
+        self.doi_label = self.in_journal.doi_label + '.' + str(self.number)
+
+    @factory.post_generation
+    def dates(self, create, extracted, **kwargs):
+        timezone.now()
+        self.start_date = Faker().date_time_between(start_date="-3y", end_date="now",
+                                                    tzinfo=pytz.UTC)
+        self.until_date = self.start_date + datetime.timedelta(weeks=26)
+
+    class Meta:
+        model = Volume
+        django_get_or_create = ('in_journal', 'number')
+
+
+class IssueFactory(factory.django.DjangoModelFactory):
+    in_volume = factory.SubFactory(VolumeFactory)
+    number = factory.Sequence(lambda n: n + 1)
+    doi_label = factory.Faker('md5')
+
+    @factory.post_generation
+    def doi(self, create, extracted, **kwargs):
+        self.doi_label = self.in_volume.doi_label + '.' + str(self.number)
+
+    @factory.post_generation
+    def dates(self, create, extracted, **kwargs):
+        timezone.now()
+        self.start_date = Faker().date_time_between(start_date=self.in_volume.start_date,
+                                                    end_date=self.in_volume.until_date,
+                                                    tzinfo=pytz.UTC)
+        self.until_date = self.start_date + datetime.timedelta(weeks=4)
+
+    class Meta:
+        model = Issue
+        django_get_or_create = ('in_volume', 'number')
+
+
+class PublicationFactory(factory.django.DjangoModelFactory):
+    accepted_submission = factory.SubFactory(PublishedSubmissionFactory)
+    paper_nr = factory.Sequence(lambda n: n)
+    pdf_file = Faker().file_name(extension='pdf')
+    in_issue = factory.SubFactory(IssueFactory)
+    submission_date = factory.Faker('date')
+    acceptance_date = factory.Faker('date')
+    publication_date = factory.Faker('date')
+    doi_label = factory.Faker('md5')
+
+    @factory.post_generation
+    def doi(self, create, extracted, **kwargs):
+        paper_nr = self.in_issue.publication_set.count() + 1
+        self.paper_nr = paper_nr
+        self.doi_label = self.in_issue.doi_label + '.' + str(paper_nr).rjust(3, '0')
+
+    @factory.post_generation
+    def submission_data(self, create, extracted, **kwargs):
+        # Content
+        self.discipline = self.accepted_submission.discipline
+        self.domain = self.accepted_submission.domain
+        self.subject_area = self.accepted_submission.subject_area
+        self.title = self.accepted_submission.title
+        self.abstract = self.accepted_submission.abstract
+
+        # Authors
+        self.author_list = self.accepted_submission.author_list
+        self.authors.add(*self.accepted_submission.authors.all())
+        self.first_author = self.accepted_submission.authors.first()
+        self.authors_claims.add(*self.accepted_submission.authors_claims.all())
+        self.authors_false_claims.add(*self.accepted_submission.authors_false_claims.all())
+
+        # Dates
+        self.submission_date = self.accepted_submission.latest_activity
+        self.acceptance_date = self.accepted_submission.latest_activity
+        self.publication_date = self.accepted_submission.latest_activity
+        self.latest_activity = self.accepted_submission.latest_activity
+
+    class Meta:
+        model = Publication
+        django_get_or_create = ('accepted_submission', )
diff --git a/journals/helpers.py b/journals/helpers.py
index c31314def9187347421ba9fd7251603d5dd688f5..d1d656c2bad92ed789800d15dbe3685f93ca4d84 100644
--- a/journals/helpers.py
+++ b/journals/helpers.py
@@ -8,6 +8,8 @@ def journal_name_abbrev_citation(journal_name):
         return 'SciPost Phys. Sel.'
     elif journal_name == 'SciPostPhysLectNotes':
         return 'SciPost Phys. Lect. Notes'
+    elif journal_name == 'SciPostPhysProc':
+        return 'SciPost Phys. Proc.'
     else:
         raise JournalNameError(journal_name)
 
diff --git a/journals/templates/journals/_base.html b/journals/templates/journals/_base.html
index f6d61a1c6c0ff323a8fc1f45b055407874b2502d..576442429b4d8176c5e5ad0a0c13a076f8f809f4 100644
--- a/journals/templates/journals/_base.html
+++ b/journals/templates/journals/_base.html
@@ -1,5 +1,7 @@
 {% extends 'scipost/base.html' %}
 
+{% load staticfiles %}
+
 {% block pagetitle %}: {{journal}}{% endblock pagetitle %}
 {% block body_class %}{{block.super}} journals{% endblock %}
 
@@ -48,9 +50,12 @@
         <div class="row my-1">
             <div class="col-12">
                 {% if journal.active %}
-                    {{journal}} is published by the SciPost Foundation under the journal doi: 10.21468/{{journal.name}} and ISSN {{journal.issn}}.
+                    <p>{{journal}} is published by the SciPost Foundation under the journal doi: 10.21468/{{journal.name}} and ISSN {{journal.issn}}.</p>
                 {% endif %}
-            </div>
+		{% if journal.doi_label == 'SciPostPhys' %}
+		<p>All content in {{ journal }} is deposited and permanently preserved in the CLOCKSS archive <a href="https://www.clockss.org/clockss/Home"><img src="{% static 'scipost/images/clockss_original_logo_boxed_ai-cropped-90.png' %}" alt="CLOCKSS logo" width="40"></a></p>
+		{% endif %}
+	    </div>
         </div>
     </footer>
 {% endblock %}
diff --git a/requirements.txt b/requirements.txt
index ba25dba1447cf01d84a54298194168b368fbd502..8a82ef7057d72bccf77dc2a8d9aeec140e2861bf 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -17,6 +17,7 @@ django-webpack-loader==0.4.1
 djangorestframework==3.5.3
 docutils==0.12
 factory-boy==2.7.0
+Faker==0.7.11
 fake-factory==0.7.2
 feedparser==5.2.1
 imagesize==0.7.1
diff --git a/scipost/factories.py b/scipost/factories.py
index 7381d6173165155478253327f9aacac1665129c7..a10f06c567ecc6caab4e3ce5b65802f4da100b88 100644
--- a/scipost/factories.py
+++ b/scipost/factories.py
@@ -4,16 +4,16 @@ import random
 from django.contrib.auth import get_user_model
 from django.contrib.auth.models import Group
 
-from django_countries.data import COUNTRIES
+from submissions.models import Submission
 
-from .models import Contributor, EditorialCollege, EditorialCollegeFellowship
+from .models import Contributor, EditorialCollege, EditorialCollegeFellowship, Remark
 from .constants import TITLE_CHOICES
 
+from django_countries.data import COUNTRIES
+from faker import Faker
 
-class ContributorFactory(factory.django.DjangoModelFactory):
-    class Meta:
-        model = Contributor
 
+class ContributorFactory(factory.django.DjangoModelFactory):
     title = random.choice(list(dict(TITLE_CHOICES).keys()))
     user = factory.SubFactory('scipost.factories.UserFactory', contributor=None)
     status = 1  # normal user
@@ -22,6 +22,10 @@ class ContributorFactory(factory.django.DjangoModelFactory):
     country_of_employment = factory.Iterator(list(COUNTRIES))
     affiliation = factory.Faker('company')
 
+    class Meta:
+        model = Contributor
+        django_get_or_create = ('user',)
+
 
 class VettingEditorFactory(ContributorFactory):
     @factory.post_generation
@@ -32,9 +36,6 @@ class VettingEditorFactory(ContributorFactory):
 
 
 class UserFactory(factory.django.DjangoModelFactory):
-    class Meta:
-        model = get_user_model()
-
     username = factory.Faker('user_name')
     password = factory.PostGenerationMethodCall('set_password', 'adm1n')
     email = factory.Faker('safe_email')
@@ -44,6 +45,9 @@ class UserFactory(factory.django.DjangoModelFactory):
     # When user object is created, associate new Contributor object to it.
     contributor = factory.RelatedFactory(ContributorFactory, 'user')
 
+    class Meta:
+        model = get_user_model()
+
     @factory.post_generation
     def groups(self, create, extracted, **kwargs):
         # If the object is not saved, we cannot use many-to-many relationship.
@@ -58,17 +62,27 @@ class UserFactory(factory.django.DjangoModelFactory):
 
 
 class EditorialCollegeFactory(factory.django.DjangoModelFactory):
+    discipline = random.choice(['Physics', 'Chemistry', 'Medicine'])
+
     class Meta:
         model = EditorialCollege
         django_get_or_create = ('discipline', )
 
-    discipline = random.choice(['Physics', 'Chemistry', 'Medicine'])
-
 
 class EditorialCollegeFellowshipFactory(factory.django.DjangoModelFactory):
-    class Meta:
-        model = EditorialCollegeFellowship
-
     college = factory.Iterator(EditorialCollege.objects.all())
     contributor = factory.Iterator(Contributor.objects.exclude(
                                    user__username='deleted').order_by('?'))
+
+    class Meta:
+        model = EditorialCollegeFellowship
+
+
+class SubmissionRemarkFactory(factory.django.DjangoModelFactory):
+    contributor = factory.Iterator(Contributor.objects.all())
+    submission = factory.Iterator(Submission.objects.all())
+    date = factory.Faker('date_time_this_decade')
+    remark = factory.lazy_attribute(lambda x: Faker().paragraph())
+
+    class Meta:
+        model = Remark
diff --git a/scipost/management/commands/populate_db.py b/scipost/management/commands/populate_db.py
index 4880a671c3f06ff63e924fa0ffdb591a89638e89..a8561b693908642e3534433312ad4616b6eb3332 100644
--- a/scipost/management/commands/populate_db.py
+++ b/scipost/management/commands/populate_db.py
@@ -1,8 +1,16 @@
 from django.core.management.base import BaseCommand
 
+from commentaries.factories import VettedCommentaryFactory
+from comments.factories import CommentaryCommentFactory, SubmissionCommentFactory,\
+                               ThesislinkCommentFactory
+from scipost.factories import SubmissionRemarkFactory
+from journals.factories import JournalFactory, IssueFactory, PublicationFactory
 from news.factories import NewsItemFactory
+from submissions.factories import EICassignedSubmissionFactory
+from theses.factories import VettedThesisLinkFactory
 
-from ...factories import ContributorFactory, EditorialCollegeFactory, EditorialCollegeFellowshipFactory
+from ...factories import ContributorFactory, EditorialCollegeFactory,\
+                         EditorialCollegeFellowshipFactory
 
 
 class Command(BaseCommand):
@@ -14,19 +22,68 @@ class Command(BaseCommand):
             default=False,
             help='Add NewsItems',
         )
+        parser.add_argument(
+            '--commentaries',
+            action='store_true',
+            dest='commentaries',
+            default=False,
+            help='Add 5 Commentaries',
+        )
+        parser.add_argument(
+            '--comments',
+            action='store_true',
+            dest='comments',
+            default=False,
+            help='Add 10 Comments',
+        )
         parser.add_argument(
             '--contributor',
             action='store_true',
             dest='contributor',
             default=False,
-            help='Add Contributors',
+            help='Add 5 Contributors',
         )
         parser.add_argument(
             '--college',
             action='store_true',
             dest='editorial-college',
             default=False,
-            help='Add Editorial College and Fellows (Contributors required)',
+            help='Add 5 Editorial College and Fellows (Contributors required)',
+        )
+        parser.add_argument(
+            '--issues',
+            action='store_true',
+            dest='issues',
+            default=False,
+            help='Add 5 sets of {Journal, Volume and Issue}',
+        )
+        parser.add_argument(
+            '--submissions',
+            action='store_true',
+            dest='submissions',
+            default=False,
+            help='Add 5 new submissions status EIC assigned',
+        )
+        parser.add_argument(
+            '--publications',
+            action='store_true',
+            dest='publications',
+            default=False,
+            help='Add 5 Publications (includes --issues action)',
+        )
+        parser.add_argument(
+            '--remarks',
+            action='store_true',
+            dest='remarks',
+            default=False,
+            help='Add 5 new Remarks linked to Submissions',
+        )
+        parser.add_argument(
+            '--theses',
+            action='store_true',
+            dest='theses',
+            default=False,
+            help='Add 5 ThesisLinks',
         )
         parser.add_argument(
             '--all',
@@ -39,24 +96,70 @@ class Command(BaseCommand):
     def handle(self, *args, **kwargs):
         if kwargs['contributor'] or kwargs['all']:
             self.create_contributors()
+        if kwargs['commentaries'] or kwargs['all']:
+            self.create_commentaries()
+        if kwargs['comments'] or kwargs['all']:
+            self.create_comments()
         if kwargs['editorial-college'] or kwargs['all']:
             self.create_editorial_college()
             self.create_editorial_college_fellows()
         if kwargs['news'] or kwargs['all']:
             self.create_news_items()
+        if kwargs['submissions'] or kwargs['all']:
+            self.create_submissions()
+        if kwargs['issues'] or kwargs['all']:
+            self.create_issues()
+        if kwargs['publications'] or kwargs['all']:
+            self.create_publications()
+        if kwargs['remarks'] or kwargs['all']:
+            self.create_remarks()
+        if kwargs['theses'] or kwargs['all']:
+            self.create_theses()
 
     def create_contributors(self):
         ContributorFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS('Successfully created Contributors.'))
+        self.stdout.write(self.style.SUCCESS('Successfully created 5 Contributors.'))
+
+    def create_commentaries(self):
+        VettedCommentaryFactory.create_batch(5)
+        self.stdout.write(self.style.SUCCESS('Successfully created 5 Commentaries.'))
+
+    def create_comments(self):
+        CommentaryCommentFactory.create_batch(3)
+        SubmissionCommentFactory.create_batch(4)
+        ThesislinkCommentFactory.create_batch(3)
+        self.stdout.write(self.style.SUCCESS('Successfully created 10 Comments.'))
 
     def create_editorial_college(self):
         EditorialCollegeFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS('Successfully created Editorial College\'s.'))
+        self.stdout.write(self.style.SUCCESS('Successfully created 5 Editorial College\'s.'))
 
     def create_editorial_college_fellows(self):
         EditorialCollegeFellowshipFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS('Successfully created Editorial College Fellows.'))
+        self.stdout.write(self.style.SUCCESS('Successfully created 5 Editorial College Fellows.'))
 
     def create_news_items(self):
         NewsItemFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS('Successfully created News items.'))
+        self.stdout.write(self.style.SUCCESS('Successfully created 5 News items.'))
+
+    def create_submissions(self):
+        EICassignedSubmissionFactory.create_batch(5)
+        self.stdout.write(self.style.SUCCESS('Successfully created 5 Submissions.'))
+
+    def create_issues(self):
+        IssueFactory.create_batch(5)
+        self.stdout.write(self.style.SUCCESS(
+                          'Successfully created 5x {Journal, Volume and Issue}.'))
+
+    def create_publications(self):
+        JournalFactory.create_batch(4)
+        PublicationFactory.create_batch(5)
+        self.stdout.write(self.style.SUCCESS('Successfully created 5 Publications.'))
+
+    def create_remarks(self):
+        SubmissionRemarkFactory.create_batch(5)
+        self.stdout.write(self.style.SUCCESS('Successfully created 5 Remarks.'))
+
+    def create_theses(self):
+        VettedThesisLinkFactory.create_batch(5)
+        self.stdout.write(self.style.SUCCESS('Successfully created 5 ThesisLinks.'))
diff --git a/scipost/static/scipost/images/clockss_original_logo_boxed_ai-cropped-90.png b/scipost/static/scipost/images/clockss_original_logo_boxed_ai-cropped-90.png
new file mode 100644
index 0000000000000000000000000000000000000000..3f3ac1910793a41e225994f3cc02175c026d8ab5
Binary files /dev/null and b/scipost/static/scipost/images/clockss_original_logo_boxed_ai-cropped-90.png differ
diff --git a/submissions/constants.py b/submissions/constants.py
index d937973d28acfbcd1e85fce61eb2ba14c0c928de..da15bf7acf1a8dbe0bcf274b62e3e41d00513f2a 100644
--- a/submissions/constants.py
+++ b/submissions/constants.py
@@ -4,6 +4,8 @@ STATUS_REVISION_REQUESTED = 'revision_requested'
 STATUS_EIC_ASSIGNED = 'EICassigned'
 STATUS_AWAITING_ED_REC = 'awaiting_ed_rec'
 STATUS_REVIEW_CLOSED = 'review_closed'
+STATUS_ACCEPTED = 'accepted'
+STATUS_PUBLISHED = 'published'
 SUBMISSION_STATUS = (
     (STATUS_UNASSIGNED, 'Unassigned, undergoing pre-screening'),
     (STATUS_RESUBMISSION_INCOMING, 'Resubmission incoming'),
@@ -21,10 +23,10 @@ SUBMISSION_STATUS = (
     ('put_to_EC_voting', 'Undergoing voting at the Editorial College'),
     (STATUS_AWAITING_ED_REC, 'Awaiting Editorial Recommendation'),
     ('EC_vote_completed', 'Editorial College voting rounded up'),
-    ('accepted', 'Publication decision taken: accept'),
+    (STATUS_ACCEPTED, 'Publication decision taken: accept'),
     ('rejected', 'Publication decision taken: reject'),
     ('rejected_visible', 'Publication decision taken: reject (still publicly visible)'),
-    ('published', 'Published'),
+    (STATUS_PUBLISHED, 'Published'),
     # If withdrawn:
     ('withdrawn', 'Withdrawn by the Authors'),
 )
diff --git a/submissions/exceptions.py b/submissions/exceptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..19c5684bd004f175ffe4169cc2938d312d66edf0
--- /dev/null
+++ b/submissions/exceptions.py
@@ -0,0 +1,6 @@
+class CycleUpdateDeadlineError(Exception):
+    def __init__(self, name):
+        self.name = name
+
+    def __str__(self):
+        return self.name
diff --git a/submissions/factories.py b/submissions/factories.py
index d67299983ce9f4c0221e1dc9d05018289950e8b2..da936a2e7a8fcbe4d2d9225a6d4a83f2b4f812da 100644
--- a/submissions/factories.py
+++ b/submissions/factories.py
@@ -1,37 +1,134 @@
 import factory
+import pytz
 
-from scipost.factories import ContributorFactory
-from journals.constants import SCIPOST_JOURNAL_PHYSICS
-from common.helpers import random_arxiv_identifier_with_version_number
+from django.utils import timezone
 
-from .constants import STATUS_UNASSIGNED, STATUS_EIC_ASSIGNED, STATUS_RESUBMISSION_INCOMING
+from scipost.models import Contributor
+from journals.constants import SCIPOST_JOURNALS_DOMAINS
+from common.helpers import random_arxiv_identifier_without_version_number, random_scipost_journal
+
+from .constants import STATUS_UNASSIGNED, STATUS_EIC_ASSIGNED, STATUS_RESUBMISSION_INCOMING,\
+                       STATUS_PUBLISHED, SUBMISSION_TYPE
 from .models import Submission
 
+from faker import Faker
+
 
 class SubmissionFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Submission
 
     author_list = factory.Faker('name')
-    submitted_by = factory.SubFactory(ContributorFactory)
-    submitted_to_journal = SCIPOST_JOURNAL_PHYSICS
-    title = factory.Faker('bs')
-    abstract = factory.Faker('text')
+    submitted_by = Contributor.objects.first()
+    submitted_to_journal = factory.Sequence(lambda n: random_scipost_journal())
+    title = factory.lazy_attribute(lambda x: Faker().sentence())
+    abstract = factory.lazy_attribute(lambda x: Faker().paragraph())
     arxiv_link = factory.Faker('uri')
-    arxiv_identifier_w_vn_nr = factory.Sequence(lambda n: random_arxiv_identifier_with_version_number())
-    domain = 'E'
+    arxiv_identifier_wo_vn_nr = factory.Sequence(
+                                    lambda n: random_arxiv_identifier_without_version_number())
+    domain = SCIPOST_JOURNALS_DOMAINS[0][0]
+    abstract = Faker().paragraph()
+    author_comments = Faker().paragraph()
+    remarks_for_editors = Faker().paragraph()
+    submission_type = factory.Iterator(SUBMISSION_TYPE, getter=lambda c: c[0])
+    is_current = True
 
+    @factory.post_generation
+    def fill_arxiv_fields(self, create, extracted, **kwargs):
+        '''Fill empty arxiv fields.'''
+        self.arxiv_link = 'https://arxiv.org/abs/%s' % self.arxiv_identifier_wo_vn_nr
+        self.arxiv_identifier_w_vn_nr = '%sv1' % self.arxiv_identifier_wo_vn_nr
+        self.arxiv_vn_nr = 1
 
-class EICassignedSubmissionFactory(SubmissionFactory):
-    status = STATUS_EIC_ASSIGNED
-    editor_in_charge = factory.SubFactory(ContributorFactory)
-    open_for_commenting = True
+    @factory.post_generation
+    def contributors(self, create, extracted, **kwargs):
+        contributors = list(Contributor.objects.order_by('?')[:4])
+
+        # Auto-add the submitter as an author
+        self.submitted_by = contributors.pop()
+
+        if not create:
+            return
+        self.authors.add(self.submitted_by)
+
+        # Add three random authors
+        for contrib in contributors:
+            self.authors.add(contrib)
+            self.author_list += ', %s %s' % (contrib.user.first_name, contrib.user.last_name)
+
+    @factory.post_generation
+    def dates(self, create, extracted, **kwargs):
+        timezone.now()
+        if kwargs.get('submission', False):
+            self.submission_date = kwargs['submission']
+            self.cycle.update_deadline()
+            return
+        self.submission_date = Faker().date_time_between(start_date="-3y", end_date="now",
+                                                         tzinfo=pytz.UTC).date()
+        self.latest_activity = Faker().date_time_between(start_date=self.submission_date,
+                                                         end_date="now", tzinfo=pytz.UTC)
+        self.cycle.update_deadline()
 
 
 class UnassignedSubmissionFactory(SubmissionFactory):
+    '''
+    This Submission is a 'new request' by a Contributor for its Submission.
+    '''
     status = STATUS_UNASSIGNED
 
 
-class ResubmittedScreeningSubmissionFactory(SubmissionFactory):
+class EICassignedSubmissionFactory(SubmissionFactory):
+    status = STATUS_EIC_ASSIGNED
+    open_for_commenting = True
+    open_for_reporting = True
+
+    @factory.post_generation
+    def eic(self, create, extracted, **kwargs):
+        '''Assign an EIC to submission.'''
+        author_ids = list(self.authors.values_list('id', flat=True))
+        self.editor_in_charge = (Contributor.objects.order_by('?')
+                                            .exclude(pk=self.submitted_by.pk)
+                                            .exclude(pk__in=author_ids).first())
+
+
+class ResubmittedSubmissionFactory(SubmissionFactory):
+    '''
+    This Submission is a newer version of a Submission which is
+    already known by the SciPost database.
+    '''
     status = STATUS_RESUBMISSION_INCOMING
-    editor_in_charge = factory.SubFactory(ContributorFactory)
+    open_for_commenting = True
+    open_for_reporting = True
+    is_resubmission = True
+
+    @factory.post_generation
+    def alter_arxiv_fields(self, create, extracted, **kwargs):
+        '''Alter arxiv fields to save as version 2.'''
+        self.arxiv_identifier_w_vn_nr = '%sv2' % self.arxiv_identifier_wo_vn_nr
+        self.arxiv_vn_nr = 2
+
+    @factory.post_generation
+    def eic(self, create, extracted, **kwargs):
+        '''Assign an EIC to submission.'''
+        author_ids = list(self.authors.values_list('id', flat=True))
+        self.editor_in_charge = (Contributor.objects.order_by('?')
+                                            .exclude(pk=self.submitted_by.pk)
+                                            .exclude(pk__in=author_ids).first())
+
+    @factory.post_generation
+    def dates(self, create, extracted, **kwargs):
+        """Overwrite the parent `dates` method to skip the update_deadline call."""
+        timezone.now()
+        if kwargs.get('submission', False):
+            self.submission_date = kwargs['submission']
+            return
+        self.submission_date = Faker().date_time_between(start_date="-3y", end_date="now",
+                                                         tzinfo=pytz.UTC).date()
+        self.latest_activity = Faker().date_time_between(start_date=self.submission_date,
+                                                         end_date="now", tzinfo=pytz.UTC)
+
+
+class PublishedSubmissionFactory(SubmissionFactory):
+    status = STATUS_PUBLISHED
+    open_for_commenting = False
+    open_for_reporting = False
diff --git a/submissions/migrations/0042_auto_20170511_2321.py b/submissions/migrations/0042_auto_20170511_2321.py
new file mode 100644
index 0000000000000000000000000000000000000000..20b3a653e5fc906c41d3159a46c85278dba8b182
--- /dev/null
+++ b/submissions/migrations/0042_auto_20170511_2321.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-05-11 21:21
+from __future__ import unicode_literals
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('submissions', '0041_auto_20170418_1022'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='submission',
+            name='submission_date',
+            field=models.DateField(default=datetime.date.today, verbose_name='submission date'),
+        ),
+    ]
diff --git a/submissions/models.py b/submissions/models.py
index 0a2b82f9ae8f5dfb2a42b6a9df8c0a3978d772e3..787754332f0ecdd4c01c8b9685b499e84333b448 100644
--- a/submissions/models.py
+++ b/submissions/models.py
@@ -78,7 +78,7 @@ class Submission(ArxivCallable, models.Model):
 
     # Metadata
     metadata = JSONField(default={}, blank=True, null=True)
-    submission_date = models.DateField(verbose_name='submission date', default=timezone.now)
+    submission_date = models.DateField(verbose_name='submission date', default=datetime.date.today)
     latest_activity = models.DateTimeField(default=timezone.now)
 
     objects = SubmissionManager()
diff --git a/submissions/templates/submissions/_submission_refereeing_invitations.html b/submissions/templates/submissions/_submission_refereeing_invitations.html
index b53073395f26521028611dcf2c7dd6495e83e7da..8af3a7a1081f5007ba03fcb6be4d6a32a8322caf 100644
--- a/submissions/templates/submissions/_submission_refereeing_invitations.html
+++ b/submissions/templates/submissions/_submission_refereeing_invitations.html
@@ -20,7 +20,7 @@
                     {% endif %}
                     <div>{{invitation.date_responded}}</div>
                 {% else %}
-                    reponse pending
+                    response pending
                 {% endif %}
             </td>
 
diff --git a/submissions/test_utils.py b/submissions/test_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..36f9a5ca7a3727542c52ff5345ea96dd197c8a3f
--- /dev/null
+++ b/submissions/test_utils.py
@@ -0,0 +1,137 @@
+import datetime
+
+from django.test import TestCase, tag
+
+from common.helpers.test import add_groups_and_permissions
+from scipost.factories import ContributorFactory
+from scipost.models import Contributor
+
+from .constants import STATUS_UNASSIGNED, STATUS_RESUBMISSION_INCOMING, STATUS_AWAITING_ED_REC,\
+                       STATUS_EIC_ASSIGNED, CYCLE_DEFAULT, CYCLE_DIRECT_REC
+from .exceptions import CycleUpdateDeadlineError
+from .factories import UnassignedSubmissionFactory, ResubmittedSubmissionFactory
+from .utils import GeneralSubmissionCycle
+
+
+class TestDefaultSubmissionCycle(TestCase):
+    '''
+    This TestCase should act as a master test to check all steps in the
+    submission's cycle: default.
+    '''
+
+    def setUp(self):
+        """Basics for all tests"""
+        self.submission_date = datetime.date.today()
+        add_groups_and_permissions()
+        ContributorFactory.create_batch(5)
+        self.new_submission = UnassignedSubmissionFactory(
+            dates__submission=self.submission_date
+        )
+
+    @tag('cycle', 'core')
+    def test_init_submission_factory_is_valid(self):
+        """Ensure valid fields for the factory."""
+        self.assertEqual(self.new_submission.status, STATUS_UNASSIGNED)
+        self.assertIsNone(self.new_submission.editor_in_charge)
+        self.assertTrue(self.new_submission.is_current)
+        self.assertFalse(self.new_submission.is_resubmission)
+        self.assertIsNot(self.new_submission.title, '')
+        self.assertIsInstance(self.new_submission.submitted_by, Contributor)
+        self.assertFalse(self.new_submission.open_for_commenting)
+        self.assertFalse(self.new_submission.open_for_reporting)
+        self.assertEqual(self.new_submission.submission_date, self.submission_date)
+
+    @tag('cycle', 'core')
+    def test_initial_cycle_required_actions_and_deadline(self):
+        """Test valid required actions for default cycle."""
+        self.assertIsInstance(self.new_submission.cycle, GeneralSubmissionCycle)
+
+        # Explicit: No actions required if no EIC is assigned yet
+        self.assertFalse(self.new_submission.cycle.get_required_actions())
+
+        # Two weeks deadline check
+        self.new_submission.cycle.update_deadline()
+        real_report_deadline = self.submission_date + datetime.timedelta(days=28)
+        self.assertEqual(self.new_submission.reporting_deadline.day, real_report_deadline.day)
+        self.assertEqual(self.new_submission.reporting_deadline.month, real_report_deadline.month)
+        self.assertEqual(self.new_submission.reporting_deadline.year, real_report_deadline.year)
+        self.assertIsInstance(self.new_submission.reporting_deadline, datetime.datetime)
+
+
+class TestResubmissionSubmissionCycle(TestCase):
+    '''
+    This TestCase should act as a master test to check all steps in the
+    submission's cycle: resubmission.
+    '''
+
+    def setUp(self):
+        """Basics for all tests"""
+        self.submission_date = datetime.date.today()
+        add_groups_and_permissions()
+        ContributorFactory.create_batch(5)
+        self.submission = ResubmittedSubmissionFactory(
+            dates__submission=self.submission_date
+        )
+
+    @tag('cycle', 'core')
+    def test_init_resubmission_factory_is_valid(self):
+        """Ensure valid fields for the factory."""
+        self.assertEqual(self.submission.status, STATUS_RESUBMISSION_INCOMING)
+        self.assertIsInstance(self.submission.editor_in_charge, Contributor)
+        self.assertTrue(self.submission.is_current)
+        self.assertTrue(self.submission.is_resubmission)
+        self.assertIsNot(self.submission.title, '')
+        self.assertIsInstance(self.submission.submitted_by, Contributor)
+        self.assertTrue(self.submission.open_for_commenting)
+        self.assertTrue(self.submission.open_for_reporting)
+        self.assertEqual(self.submission.submission_date, self.submission_date)
+        self.assertEqual(self.submission.refereeing_cycle, CYCLE_DEFAULT)
+
+    @tag('cycle', 'core')
+    def test_initial_cycle_required_actions_and_deadline(self):
+        """Test valid required actions for default cycle."""
+        self.assertRaises(CycleUpdateDeadlineError, self.submission.cycle.update_deadline)
+
+        # Update status for default cycle to check new status
+        self.submission.cycle.update_status()
+        self.assertEqual(self.submission.status, STATUS_EIC_ASSIGNED)
+
+
+class TestResubmissionDirectSubmissionCycle(TestCase):
+    '''
+    This TestCase should act as a master test to check all steps in the
+    submission's cycle: resubmission (cycle: DIRECT_RECOMMENDATION).
+    '''
+
+    def setUp(self):
+        """Basics for all tests"""
+        self.submission_date = datetime.date.today()
+        add_groups_and_permissions()
+        ContributorFactory.create_batch(5)
+        self.submission = ResubmittedSubmissionFactory(
+            dates__submission=self.submission_date,
+            refereeing_cycle=CYCLE_DIRECT_REC
+        )
+
+    @tag('cycle', 'core')
+    def test_init_resubmission_factory_is_valid(self):
+        """Ensure valid fields for the factory."""
+        self.assertEqual(self.submission.status, STATUS_RESUBMISSION_INCOMING)
+        self.assertIsInstance(self.submission.editor_in_charge, Contributor)
+        self.assertTrue(self.submission.is_current)
+        self.assertTrue(self.submission.is_resubmission)
+        self.assertIsNot(self.submission.title, '')
+        self.assertIsInstance(self.submission.submitted_by, Contributor)
+        self.assertTrue(self.submission.open_for_commenting)
+        self.assertTrue(self.submission.open_for_reporting)
+        self.assertEqual(self.submission.submission_date, self.submission_date)
+        self.assertEqual(self.submission.refereeing_cycle, CYCLE_DIRECT_REC)
+
+    @tag('cycle', 'core')
+    def test_initial_cycle_required_actions_and_deadline(self):
+        """Test valid required actions for default cycle."""
+        self.assertRaises(CycleUpdateDeadlineError, self.submission.cycle.update_deadline)
+
+        # Update status for default cycle to check new status
+        self.submission.cycle.update_status()
+        self.assertEqual(self.submission.status, STATUS_AWAITING_ED_REC)
diff --git a/submissions/utils.py b/submissions/utils.py
index d713236f3f2b66dab3fd04300962da4ece33e9a6..29ed7e85be4febc089c51bd4b476b66f3db4c21a 100644
--- a/submissions/utils.py
+++ b/submissions/utils.py
@@ -7,6 +7,7 @@ from django.utils import timezone
 from .constants import NO_REQUIRED_ACTION_STATUSES,\
                        STATUS_REVISION_REQUESTED, STATUS_EIC_ASSIGNED,\
                        STATUS_RESUBMISSION_INCOMING, STATUS_AWAITING_ED_REC
+from .exceptions import CycleUpdateDeadlineError
 
 from scipost.utils import EMAIL_FOOTER
 from common.utils import BaseMailUtil
@@ -112,11 +113,19 @@ class BaseSubmissionCycle:
                 SubmissionUtils.reinvite_referees_email()
 
     def update_deadline(self, period=None):
+        """
+        Reset the reporting deadline according to current datetime and default cycle length.
+        New reporting deadline may be explicitly given as datetime instance.
+        """
+        if self.submission.status == STATUS_RESUBMISSION_INCOMING:
+            raise CycleUpdateDeadlineError('Submission has invalid status: %s'
+                                           % self.submission.status)
         delta_d = period or self.default_days
         deadline = timezone.now() + datetime.timedelta(days=delta_d)
         self.submission.reporting_deadline = deadline
         self.submission.save()
 
+
     def get_required_actions(self):
         '''Return list of the submission its required actions'''
         if not self.updated_action:
diff --git a/theses/factories.py b/theses/factories.py
index fe9091788560fdc69f34f7b65519f0d0772157b9..76bf49969a745b620acbf464707235056f985e73 100644
--- a/theses/factories.py
+++ b/theses/factories.py
@@ -1,11 +1,19 @@
 import factory
 
+from django.utils import timezone
+
 from common.helpers.factories import FormFactory
+from journals.constants import SCIPOST_JOURNALS_DOMAINS
+from scipost.constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS
 from scipost.factories import ContributorFactory
 
 from .models import ThesisLink
 from .forms import VetThesisLinkForm
-from .constants import MASTER_THESIS
+from .constants import THESIS_TYPES
+
+from faker import Faker
+
+timezone.now()
 
 
 class ThesisLinkFactory(factory.django.DjangoModelFactory):
@@ -13,15 +21,17 @@ class ThesisLinkFactory(factory.django.DjangoModelFactory):
         model = ThesisLink
 
     requested_by = factory.SubFactory(ContributorFactory)
-    type = MASTER_THESIS
-    title = factory.Faker('bs')
+    type = factory.Iterator(THESIS_TYPES, getter=lambda c: c[0])
+    domain = factory.Iterator(SCIPOST_JOURNALS_DOMAINS, getter=lambda c: c[0])
+    discipline = factory.Iterator(SCIPOST_DISCIPLINES, getter=lambda c: c[0])
+    subject_area = factory.Iterator(SCIPOST_SUBJECT_AREAS[0][1], getter=lambda c: c[0])
+    title = factory.Faker('text')
     pub_link = factory.Faker('uri')
     author = factory.Faker('name')
     supervisor = factory.Faker('name')
     institution = factory.Faker('company')
     defense_date = factory.Faker('date')
-    abstract = factory.Faker('text')
-    domain = 'ET'
+    abstract = factory.lazy_attribute(lambda x: Faker().paragraph())
 
 
 class VettedThesisLinkFactory(ThesisLinkFactory):