diff --git a/requirements.txt b/requirements.txt
index 0b298f8..f7c70e9 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,1 +1,1 @@
-EGCG-Core==0.7.3
+EGCG-Core==0.8.1
diff --git a/scripts/assign_container_name.py b/scripts/assign_container_name.py
index 98d46c6..2952a84 100644
--- a/scripts/assign_container_name.py
+++ b/scripts/assign_container_name.py
@@ -3,41 +3,42 @@ from EPPs.common import StepEPP, step_argparser
 
 
 class AssignContainerName(StepEPP):
-    # assigns the container automatically for the FluidX Transfer from Rack to Plate step using rule of Project+P+9.
-    # Checks to see if thecontainer name has already been used.
+    """
+    Assigns a container name for, e.g, the FluidX Transfer from Rack to Plate step in the format
+    '{project_id}P{number}. Automatically checks previously used container names to ensure a new name is used.
+    """
 
     def _run(self):
-        # lists all of the output artifacts for the process.
-        processOutputs = list(self.process.all_outputs(unique=True))
-
-        # loops through each artifact, when an analyte type artifact is found it updates the container name with the rule stated above then breaks the loop
-        # as this only needs to occur once as the step can only process a single rack of tubes from a single project.
-        for p in processOutputs:
+        """
+        Update each analyte-type artifact with a new, unused container name. This only needs to occur once as the step
+        can only process a single rack of tubes from a single project.
+        """
+        for p in list(self.process.all_outputs(unique=True)):
             if p.output_type == 'Analyte':
                
                 project = p.samples[0].project.name
-                new_container_name = self.findAvailableContainer(project=project, count=1)
+                new_container_name = self.find_available_container(project)
                 p.container.name = new_container_name
                 p.container.put()
 
                 break
 
-
-
-
-    def findAvailableContainer(self, project, count):
-        # checks to see if the first container name is available and then recurses until it finds an available container name
-
-        if count>999:
+    def find_available_container(self, project, count=1):
+        """
+        Check to see if a container name is available, and recurse with incremented container numbers until an available
+        container name is found.
+        :param str project:
+        :param int count:
+        """
+        if count > 999:
             raise ValueError('Cannot allocate more than 999 containers')
 
-        new_name=project+'P%03d' % (count)
+        new_name = project + 'P%03d' % count
 
-        if self.lims.get_artifacts(containername=new_name) == []:
+        if not self.lims.get_artifacts(containername=new_name):
             return new_name
         else:
-            return self.findAvailableContainer(project=project, count=count+1)
-
+            return self.find_available_container(project, count=count + 1)
 
 
 def main():
@@ -48,4 +49,4 @@ def main():
 
 
 if __name__ == '__main__':
-    main()
\ No newline at end of file
+    main()
diff --git a/scripts/assign_workflow_preseqlab.py b/scripts/assign_workflow_preseqlab.py
index 59cc10e..0cd2fa7 100644
--- a/scripts/assign_workflow_preseqlab.py
+++ b/scripts/assign_workflow_preseqlab.py
@@ -5,12 +5,12 @@ from EPPs.common import StepEPP, step_argparser, get_workflow_stage, find_newest
 class AssignWorkflowPreSeqLab(StepEPP):
 
     def _run(self):
-        artifact_to_route = set()
+        artifacts_to_route = set()
         for art in self.artifacts:
             sample = art.samples[0]
             if sample.udf.get("Proceed To SeqLab") and not sample.udf.get("2D Barcode"):
                 # checks to see if sample is in plate or fluidX tube
-                artifact_to_route.add(sample.artifact)
+                artifacts_to_route.add(sample.artifact)
 
             elif sample.udf.get("Proceed To SeqLab") and sample.udf.get("2D Barcode"):
                 artifact = find_newest_artifact_originating_from(
@@ -18,12 +18,12 @@ class AssignWorkflowPreSeqLab(StepEPP):
                     process_type="FluidX Transfer From Rack Into Plate EG 1.0 ST",
                     sample_name=sample.name
                 )
-                artifact_to_route.add(artifact)
+                artifacts_to_route.add(artifact)
 
-        if artifact_to_route:
+        if artifacts_to_route:
             # Only route artifacts if there are any
             stage = get_workflow_stage(self.lims, "PreSeqLab EG 6.0", "Sequencing Plate Preparation EG 2.0")
-            self.lims.route_artifacts(list(artifact_to_route), stage_uri=stage.uri)
+            self.lims.route_artifacts(list(artifacts_to_route), stage_uri=stage.uri)
 
 
 def main():
@@ -34,4 +34,3 @@ def main():
 
 if __name__ == "__main__":
     main()
-
diff --git a/scripts/assign_workflow_receive_sample.py b/scripts/assign_workflow_receive_sample.py
index ccc42be..7d31130 100644
--- a/scripts/assign_workflow_receive_sample.py
+++ b/scripts/assign_workflow_receive_sample.py
@@ -1,35 +1,32 @@
 #!/usr/bin/env python
 from EPPs.common import StepEPP, step_argparser, get_workflow_stage
 
-class AssignWorkflowReceiveSample(StepEPP):
-    #Assign received plate to either User Prepared Library Batch or Spectramax Picogreen
 
+class AssignWorkflowReceiveSample(StepEPP):
+    """
+    Assigns a received plate to either User Prepared Library Batch or Spectramax Picogreen, depending on the contents of
+    the UDF 'User Prepared Library'
+    """
     def _run(self):
-        artifact_to_route_userprepared = set()
-        artifact_to_route_preseqlab = set()
-
+        artifacts_to_route_userprepared = set()
+        artifacts_to_route_preseqlab = set()
 
         for art in self.artifacts:
             sample = art.samples[0]
             if sample.udf.get("User Prepared Library") == "Yes":
-                artifact_to_route_userprepared.add(art)
-            # assigns the received sample to User Prepared Library Batch if is a user prepared library
+                artifacts_to_route_userprepared.add(art)
+            else:
+                artifacts_to_route_preseqlab.add(art)
 
-
-            elif not sample.udf.get("User Prepared Library"):
-                artifact_to_route_preseqlab.add(art)
-                # assigns the received sample to PreSeqLab if NOT an user prepared library
-
-        if artifact_to_route_userprepared:
+        if artifacts_to_route_userprepared:
             # Only route artifacts if there are any artifacts to go to PCR-Free
             stage = get_workflow_stage(self.lims, "User Prepared Library Batch EG1.0 WF", "User Prepared Library Batch EG 1.0 ST")
-            self.lims.route_artifacts(list(artifact_to_route_userprepared), stage_uri=stage.uri)
+            self.lims.route_artifacts(list(artifacts_to_route_userprepared), stage_uri=stage.uri)
 
-        if artifact_to_route_preseqlab:
+        if artifacts_to_route_preseqlab:
             # Only route artifacts if there are any artifacts to go to Nano
             stage = get_workflow_stage(self.lims, "PreSeqLab EG 6.0", "Spectramax Picogreen EG 6.0")
-            self.lims.route_artifacts(list(artifact_to_route_preseqlab), stage_uri=stage.uri)
-
+            self.lims.route_artifacts(list(artifacts_to_route_preseqlab), stage_uri=stage.uri)
 
 
 def main():
diff --git a/scripts/assign_workflow_seqlab_quantstudio.py b/scripts/assign_workflow_seqlab_quantstudio.py
index 5c8802e..9f964ce 100644
--- a/scripts/assign_workflow_seqlab_quantstudio.py
+++ b/scripts/assign_workflow_seqlab_quantstudio.py
@@ -2,65 +2,51 @@
 from EPPs.common import StepEPP, step_argparser, get_workflow_stage, find_newest_artifact_originating_from
 
 
-def get_parent_process_id(art):
-    return art.parent_process.id
-
-
 class AssignWorkflowSeqLabQuantStudio(StepEPP):
-    '''Assigns plate submitted samples or FluidX derived artifacts to the correct SeqLab Workflow and
-    assigns to QuantStudio workflow if required based on the contents of submitted sample UDFs.
-    "Prep Workflow", "Species", "Skip genotyping for (human) sample?"'''
-
+    """
+    Assigns plates of submitted samples or FluidX derived artifacts to the correct SeqLab Workflow and assigns to the
+    QuantStudio workflow if required based on UDFs "Prep Workflow", "Species" and "Skip genotyping for (human) sample?"
+    """
     def _run(self):
-        artifact_to_route_pcr_free = set()
-        artifact_to_route_nano = set()
-        artifact_to_route_quant = set()
+        artifacts_to_route_pcr_free = set()
+        artifacts_to_route_nano = set()
+        artifacts_to_route_quant = set()
 
         for art in self.output_artifacts:
             sample = art.samples[0]
             if sample.udf.get("Prep Workflow") == "TruSeq PCR-Free DNA Sample Prep":
-                artifact_to_route_pcr_free.add(art)
-            # assigns the normalised batch plate to the TruSeq PCR-Free workflow
-
+                artifacts_to_route_pcr_free.add(art)
 
             elif sample.udf.get("Prep Workflow") == "TruSeq Nano DNA Sample Prep":
-                artifact_to_route_nano.add(art)
-                # assigns the normalised batch plate to the TruSeq Nano workflow
-
-            if sample.udf.get("Species") == "Homo sapiens" and not sample.udf.get("Skip genotyping for (human) sample?") \
-                    or sample.udf.get("Species") == "Human" and not sample.udf.get(
-                        "Skip genotyping for (human) sample?"):
+                artifacts_to_route_nano.add(art)
 
-                # assigns either the submitted sample plate or the FluidX derived plate to the QuantStudio
-                # workflow if a human samples and the lab manager has not selected "Skip genotyping for (human)
-                # sample?"
+            if sample.udf.get("Species") in ("Homo sapiens", "Human") and not sample.udf.get("Skip genotyping for (human) sample?"):
+                # assign either the submitted sample plate or the FluidX derived plate to the QuantStudio
+                # workflow if human samples and the lab manager has not selected "Skip genotyping for (human) sample?"
 
                 if not sample.udf.get("2D Barcode"):
-                    # checks to see if sample is in plate or fluidX tube
-                    artifact_to_route_quant.add(sample.artifact)
+                    # check to see if sample is in plate or fluidX tube
+                    artifacts_to_route_quant.add(sample.artifact)
 
-                elif sample.udf.get("2D Barcode"):
+                else:
                     artifact = find_newest_artifact_originating_from(
                         self.lims,
                         process_type="FluidX Transfer From Rack Into Plate EG 1.0 ST",
                         sample_name=sample.name
                     )
-                    artifact_to_route_quant.add(artifact)
+                    artifacts_to_route_quant.add(artifact)
 
-        if artifact_to_route_pcr_free:
-            # Only route artifacts if there are any artifacts to go to PCR-Free
+        if artifacts_to_route_pcr_free:
             stage = get_workflow_stage(self.lims, "TruSeq PCR-Free DNA Sample Prep", "Visual QC")
-            self.lims.route_artifacts(list(artifact_to_route_pcr_free), stage_uri=stage.uri)
+            self.lims.route_artifacts(list(artifacts_to_route_pcr_free), stage_uri=stage.uri)
 
-        if artifact_to_route_nano:
-            # Only route artifacts if there are any artifacts to go to Nano
+        if artifacts_to_route_nano:
             stage = get_workflow_stage(self.lims, "TruSeq Nano DNA Sample Prep", "Visual QC")
-            self.lims.route_artifacts(list(artifact_to_route_nano), stage_uri=stage.uri)
+            self.lims.route_artifacts(list(artifacts_to_route_nano), stage_uri=stage.uri)
 
-        if artifact_to_route_quant:
-            # Only route artifacts if there are any artifacts to go to Nano
+        if artifacts_to_route_quant:
             stage = get_workflow_stage(self.lims, "QuantStudio EG1.0", "QuantStudio Plate Preparation EG1.0")
-            self.lims.route_artifacts(list(artifact_to_route_quant), stage_uri=stage.uri)
+            self.lims.route_artifacts(list(artifacts_to_route_quant), stage_uri=stage.uri)
 
 
 def main():
diff --git a/scripts/assign_workflow_user_library.py b/scripts/assign_workflow_user_library.py
index 64cb73f..141d7ae 100644
--- a/scripts/assign_workflow_user_library.py
+++ b/scripts/assign_workflow_user_library.py
@@ -1,9 +1,11 @@
 #!/usr/bin/env python
 from EPPs.common import StepEPP, step_argparser, get_workflow_stage
 
-class AssignWorkflowUserPreparedLibrary(StepEPP):
-    #Assign plate created in User Prepared Library to either Nano or PCR Free workflow
 
+class AssignWorkflowUserPreparedLibrary(StepEPP):
+    """
+    Assigns a plate created in User Prepared Library to either the Nano or PCR Free workflow.
+    """
     def _run(self):
         artifact_to_route_pcr_free = set()
         artifact_to_route_nano = set()
@@ -12,25 +14,19 @@ class AssignWorkflowUserPreparedLibrary(StepEPP):
             sample = art.samples[0]
             if sample.udf.get("Prep Workflow") == "TruSeq PCR-Free DNA Sample Prep":
                 artifact_to_route_pcr_free.add(art)
-            # assigns the normalised batch plate to the TruSeq PCR-Free workflow
-
 
             elif sample.udf.get("Prep Workflow") == "TruSeq Nano DNA Sample Prep":
                 artifact_to_route_nano.add(art)
-                # assigns the normalised batch plate to the TruSeq Nano workflow
 
         if artifact_to_route_pcr_free:
-            # Only route artifacts if there are any artifacts to go to PCR-Free
             stage = get_workflow_stage(self.lims, "TruSeq PCR-Free DNA Sample Prep", "SEMI-AUTOMATED - Make and Read qPCR Quant")
             self.lims.route_artifacts(list(artifact_to_route_pcr_free), stage_uri=stage.uri)
 
         if artifact_to_route_nano:
-            # Only route artifacts if there are any artifacts to go to Nano
             stage = get_workflow_stage(self.lims, "TruSeq Nano DNA Sample Prep", "SEMI-AUTOMATED - Make LQC & Caliper GX QC")
             self.lims.route_artifacts(list(artifact_to_route_nano), stage_uri=stage.uri)
 
 
-
 def main():
     p = step_argparser()
     args = p.parse_args()
diff --git a/scripts/convert_and_dispatch_genotypes.py b/scripts/convert_and_dispatch_genotypes.py
index 528565e..4cb8a39 100644
--- a/scripts/convert_and_dispatch_genotypes.py
+++ b/scripts/convert_and_dispatch_genotypes.py
@@ -1,15 +1,12 @@
 #!/usr/bin/env python
 import csv
-import sys
 from os import remove
 from os.path import join, dirname, abspath
 from collections import defaultdict
 from egcg_core.config import Configuration
 from egcg_core.app_logging import AppLogger, logging_default as log_cfg
-
-sys.path.append(dirname(dirname(abspath(__file__))))
-from EPPs.common import StepEPP, step_argparser
 import EPPs
+from EPPs.common import StepEPP, step_argparser
 
 etc_path = join(abspath(dirname(EPPs.__file__)), 'etc')
 snp_cfg = Configuration(join(etc_path, 'SNPs_definition.yml'))
diff --git a/scripts/create_reagents_for_run.py b/scripts/create_reagents_for_run.py
index a304f56..6e540e4 100644
--- a/scripts/create_reagents_for_run.py
+++ b/scripts/create_reagents_for_run.py
@@ -57,10 +57,18 @@ class CreateReagentForRun(StepEPP):
             else:
                 reagent_kits = self.lims.get_reagent_kits(name=reagent_kit_map[name])
                 if len(reagent_kits) != 1:
-                    raise Exception('Found %s reagent kits for name %s' % (len(reagent_kits), reagent_kit_map[name]))
-                reagent_lot = ReagentLot.create(self.lims, reagent_kit=reagent_kits[0], name=name, lot_number=lot, expiry_date=time.strftime('%Y-%m,-%d'), status='active')
+                    raise ValueError('Found %s reagent kits for name %s' % (len(reagent_kits), reagent_kit_map[name]))
 
-            print('Create reagent %s: %s' % (name, lot))
+                ReagentLot.create(
+                    self.lims,
+                    reagent_kit=reagent_kits[0],
+                    name=name,
+                    lot_number=lot,
+                    expiry_date=time.strftime('%Y-%m,-%d'),
+                    status='active'
+                )
+
+            print('Created reagent %s: %s' % (name, lot))
 
 
 def main():
diff --git a/scripts/email_data_release.py b/scripts/email_data_release.py
index 80303c4..35c7305 100644
--- a/scripts/email_data_release.py
+++ b/scripts/email_data_release.py
@@ -5,58 +5,40 @@ from EPPs.config import load_config
 
 
 class DataReleaseEmail(SendMailEPP):
+    """
+    Notifies the relevant teams that data for a project has been released. Also sends a reminder to send a customer
+    survey depending on UDF values.
+    """
 
     def _run(self):
-        if len(self.projects)>1:  # check if more than one project in step, only one permitted
+        if len(self.projects) > 1:
             raise ValueError('More than one project present in step. Only one project per step permitted')
 
-        # Create the message to notify team that data has been released
         msg = 'Hi,\n\nData for {sample_count} sample(s) has been released for {project} at:\n\n{link}\n\nKind regards,\nClarityX'
-
-        # fill in message with parameters
         msg = msg.format(
-            link='https://'+platform.node() + '/clarity/work-details/' + self.step_id[3:],
+            link='https://' + platform.node() + '/clarity/work-details/' + self.step_id[3:],
             sample_count=len(self.samples),
-            project=self.projects[0].name,
+            project=self.projects[0].name
         )
-        subject = ', '.join([p.name for p in self.projects]) + ': Data Released'
-
-        # Send email to list of persons specified in the projects-facility-finance_only section of config
+        subject = ', '.join(p.name for p in self.projects) + ': Data Released'
         self.send_mail(subject, msg, config_name='projects-facility-finance_only')
 
-        # if "Request Customer Survey (Final Data Release)" step UDF completed as "Yes"
-        # send an additional email to notify business team to send customer survey
-        # and reminder to finance that the final release has occurred
-        if self.process.udf.get("Request Customer Survey (Final Data Release)") == "Yes":
+        if self.process.udf.get('Request Customer Survey (Final Data Release)') == 'Yes':
             msg2 = 'Hi,\n\nThe final data release has occurred for {project}. Please request a customer survey.\n\nKind regards,\nClarityX'
+            msg2 = msg2.format(project=self.projects[0].name)
 
-            # fill in message with parameters
-            msg2 = msg2.format(
-                sample_count=len(self.samples),
-                project=self.projects[0].name,
-            )
             subject2 = self.projects[0].name + ': Request Customer Survey - Final Data Released'
             self.send_mail(subject2, msg2, config_name='projects-facility-finance-bd_only')
 
 
 def main():
-    # Ge the default command line options
     p = step_argparser()
-
-    # Parse command line options
     args = p.parse_args()
-
-    # Load the config from the default location
     load_config()
 
-    # Setup the EPP
-    action = DataReleaseEmail(
-        args.step_uri, args.username, args.password, args.log_file,
-    )
-
-    # Run the EPP
+    action = DataReleaseEmail(args.step_uri, args.username, args.password, args.log_file)
     action.run()
 
 
-if __name__ == "__main__":
+if __name__ == '__main__':
     main()
diff --git a/scripts/email_data_trigger.py b/scripts/email_data_trigger.py
index eac8c99..0a3d094 100644
--- a/scripts/email_data_trigger.py
+++ b/scripts/email_data_trigger.py
@@ -1,26 +1,26 @@
 #!/usr/bin/env python
-from collections import defaultdict
 import platform
 from EPPs.common import step_argparser, SendMailEPP
 from EPPs.config import load_config
 
 
 class DataReleaseEmailAndUpdateEPP(SendMailEPP):
+    """Notifies the bioinformatics team to release data for a project."""
 
     def _run(self):
-        if len(self.projects)>1:
+        if len(self.projects) > 1:
             raise ValueError('More than one project present in step. Only one project per step permitted')
 
         data_download_contacts = []
         # There are up to 5 contacts entered in the step.
-        for count in range(1,6):
-            udf_name1 = "Data Download Contact Name "+str(count)
-            udf_name2 = "Is Contact "+str(count)+" A New or Existing User?"
+        for count in range(1, 6):
+            udf_name1 = 'Data Download Contact Name %s' % count
+            udf_name2 = 'Is Contact %s A New or Existing User?' % count
             if self.process.udf.get(udf_name1):
                 data_download_contacts.append(
-                    '%s (%s)' % (self.process.udf.get(udf_name1), self.process.udf.get(udf_name2) )
+                    '%s (%s)' % (self.process.udf.get(udf_name1), self.process.udf.get(udf_name2))
                 )
-        # Create the message
+
         msg = '''Hi Bioinformatics,
 
 Please release the data for {sample_count} sample(s) from project {project} shown at the link below:
@@ -33,37 +33,27 @@ The data contacts are:
 
 Kind regards,
 ClarityX'''
-        # fill in message with parameters
+
         msg = msg.format(
-            link='https://'+platform.node() + '/clarity/work-details/' + self.step_id[3:],
+            link='https://' + platform.node() + '/clarity/work-details/' + self.step_id[3:],
             sample_count=len(self.samples),
             project=self.projects[0].name,
             data_download_contacts='\n'.join(data_download_contacts)
         )
-        subject = ', '.join([p.name for p in self.projects]) + ': Please release data'
+        subject = ', '.join(p.name for p in self.projects) + ': Please release data'
 
         # Send email to list of persons specified in the default section of config
         self.send_mail(subject, msg)
 
 
 def main():
-    # Get the default command line options
     p = step_argparser()
-
-    # Parse command line options
     args = p.parse_args()
-
-    # Load the config from the default location
     load_config()
 
-    # Setup the EPP
-    action = DataReleaseEmailAndUpdateEPP(
-        args.step_uri, args.username, args.password, args.log_file,
-    )
-
-    # Run the EPP
+    action = DataReleaseEmailAndUpdateEPP(args.step_uri, args.username, args.password, args.log_file)
     action.run()
 
 
-if __name__ == "__main__":
-    main()
\ No newline at end of file
+if __name__ == '__main__':
+    main()
diff --git a/scripts/email_fluidx_sample_receipt.py b/scripts/email_fluidx_sample_receipt.py
index db607f6..0abb1a1 100644
--- a/scripts/email_fluidx_sample_receipt.py
+++ b/scripts/email_fluidx_sample_receipt.py
@@ -5,26 +5,22 @@ from EPPs.config import load_config
 
 
 class FluidXSampleReceiptEmail(SendMailEPP):
+    """Sends an email to request FluidX manifest parsing by the project team"""
 
     def _run(self):
-        if len(self.projects) > 1:  # check if more than one project in step, only one permitted
+        if len(self.projects) > 1:
             raise ValueError('More than one project present in step. Only one project per step permitted')
 
-        # Create the message to notify the lab team, projects and finance that samples have arrived at the facility
         msg = 'Hi,\n\n{sample_count} sample(s) have been received for project {project} at:\n\n{link}\n\nKind regards,\nClarityX'
-
-        # fill in message with parameters
         msg = msg.format(
-            link='https://'+platform.node() + '/clarity/work-details/' + self.step_id[3:],
+            link='https://' + platform.node() + '/clarity/work-details/' + self.step_id[3:],
             sample_count=len(self.samples),
             project=self.projects[0].name,
         )
-        subject = ', '.join([p.name for p in self.projects]) + ': FluidX Tube Received'
+        subject = ', '.join(p.name for p in self.projects) + ': FluidX Tube Received'
 
-        # Send email to list of persons specified in the projects-lab-finance_only section of config
         self.send_mail(subject, msg, config_name='projects-lab-finance_only')
 
-        # Create the message to request manifest parsing by the project team
         msg2 = '''Hi,
 
 The manifest should now be parsed for project {project} go to the queue for step FluidX Manifest Parsing EG 1.0 ST at:
@@ -34,33 +30,19 @@ The manifest should now be parsed for project {project} go to the queue for step
 Kind regards,
 ClarityX'''
 
-        # fill in message with parameters
-        msg2 = msg2.format(
-            link='https://' + platform.node() + '/clarity/queue/752',
-            project=self.projects[0].name,
-        )
-        subject2 = ', '.join([p.name for p in self.projects]) + ': Parse Manifest Required (FluidX)'
+        msg2 = msg2.format(link='https://' + platform.node() + '/clarity/queue/752', project=self.projects[0].name)
+        subject2 = ', '.join(p.name for p in self.projects) + ': Parse Manifest Required (FluidX)'
         self.send_mail(subject2, msg2, config_name='projects_only')
 
 
 def main():
-    # Ge the default command line options
     p = step_argparser()
-
-    # Parse command line options
     args = p.parse_args()
-
-    # Load the config from the default location
     load_config()
 
-    # Setup the EPP
-    action = FluidXSampleReceiptEmail(
-        args.step_uri, args.username, args.password, args.log_file,
-    )
-
-    # Run the EPP
+    action = FluidXSampleReceiptEmail(args.step_uri, args.username, args.password, args.log_file)
     action.run()
 
 
-if __name__ == "__main__":
+if __name__ == '__main__':
     main()
diff --git a/scripts/email_receive_sample.py b/scripts/email_receive_sample.py
index 9a5fc96..793c057 100644
--- a/scripts/email_receive_sample.py
+++ b/scripts/email_receive_sample.py
@@ -5,44 +5,30 @@ from EPPs.config import load_config
 
 
 class ReceiveSampleEmail(SendMailEPP):
+    """Notifies the relevant teams that samples for a project have been received"""
 
     def _run(self):
-        if len(self.projects)>1: #  check if more than one project in step, only one permitted
+        if len(self.projects) > 1:
             raise ValueError('More than one project present in step. Only one project per step permitted')
 
-        # Create the message
         msg = 'Hi,\n\n{sample_count} sample(s) have been received for {project} at:\n\n{link}\n\nKind regards,\nClarityX'
-
-        # fill in message with parameters
         msg = msg.format(
-            link='https://'+platform.node() + '/clarity/work-details/' + self.step_id[3:],
+            link='https://' + platform.node() + '/clarity/work-details/' + self.step_id[3:],
             sample_count=len(self.samples),
-            project=self.projects[0].name,
+            project=self.projects[0].name
         )
         subject = self.projects[0].name + ': Plate Received'
-
-        # Send email to list of persons specified in the projects-facility-lab-finance_only section of config
         self.send_mail(subject, msg, config_name='projects-facility-lab-finance_only')
 
 
 def main():
-    # Ge the default command line options
     p = step_argparser()
-
-    # Parse command line options
     args = p.parse_args()
-
-    # Load the config from the default location
     load_config()
 
-    # Setup the EPP
-    action = ReceiveSampleEmail(
-        args.step_uri, args.username, args.password, args.log_file,
-    )
-
-    # Run the EPP
+    action = ReceiveSampleEmail(args.step_uri, args.username, args.password, args.log_file)
     action.run()
 
 
-if __name__ == "__main__":
-    main()
\ No newline at end of file
+if __name__ == '__main__':
+    main()
diff --git a/scripts/prepare_discard_plate.py b/scripts/prepare_discard_plate.py
index 00f1e9b..c7ad4bb 100644
--- a/scripts/prepare_discard_plate.py
+++ b/scripts/prepare_discard_plate.py
@@ -61,13 +61,15 @@ class FindPlateToRoute(StepEPP):
         step_artifacts = self.process.all_inputs()
         self.lims.get_batch(step_artifacts)
 
-        # Fetch the sample associated with these artifacts
+        # Fetch the samples associated with these artifacts
         samples = [a.samples[0] for a in step_artifacts]
         self.lims.get_batch(samples)
         self.info('Found %d Samples in the step', len(samples))
+
         # Fetch the all the artifacts associated with these samples
         step_associated_artifacts = fetch_all_artifacts_for_samples(self.lims, samples)
         self.info('Found %d Analytes (derived samples)', len(step_associated_artifacts))
+
         # List all the containers and retrieve them
         containers = list(set([a.container for a in step_associated_artifacts]))
         batch_limit(self.lims, containers)

User prepared library UDF handling
In assign_workflow_receive_sample, the following logic is done:
```python
if sample.udf.get('User Prepared Library') == 'Yes':
    artifacts_to_route_userprepared.add(art)
elif not sample.udf.get('User Prepared Library'):
    artifacts_to_route_preseqlab.add(art)
```
This relies on User Prepared Library being either 'Yes' or not populated, which is expected to be the case in the Lims, but if there is a sample with the UDF `{'User Prepared Library': 'No'}`, nothing will happen. It would be more robust if the `elif` was an `else`.

diff --git a/tests/test_assign_container_name.py b/tests/test_assign_container_name.py
index 07b4664..1bf2c61 100644
--- a/tests/test_assign_container_name.py
+++ b/tests/test_assign_container_name.py
@@ -41,8 +41,8 @@ class TestAssignContainerName(TestEPP):
 
     def test_findAvailableContainer(self):
 
-        with patch.object(self.epp.lims, 'get_artifacts',side_effect=[['something'], ['something'], []]):
-            assert self.epp.findAvailableContainer(project='project1', count=1) == 'project1P003'
+        with patch.object(self.epp.lims, 'get_artifacts', side_effect=[['something'], ['something'], []]):
+            assert self.epp.find_available_container(project='project1', count=1) == 'project1P003'
 
         with patch.object(self.epp.lims, 'get_artifacts', side_effect=[['a']] * 500 + [[]]):
-            assert self.epp.findAvailableContainer(project='project1', count=1) == 'project1P501'
+            assert self.epp.find_available_container(project='project1', count=1) == 'project1P501'
diff --git a/tests/test_assign_to_workflow_preseqlab.py b/tests/test_assign_to_workflow_preseqlab.py
index 7aba75b..00b7614 100644
--- a/tests/test_assign_to_workflow_preseqlab.py
+++ b/tests/test_assign_to_workflow_preseqlab.py
@@ -1,14 +1,13 @@
-from EPPs.common import StepEPP
 from scripts.assign_workflow_preseqlab import AssignWorkflowPreSeqLab
 from tests.test_common import TestEPP, fake_artifact
-from unittest.mock import Mock, patch, PropertyMock, MagicMock
+from unittest.mock import Mock, patch, PropertyMock
 
 
 def fake_all_inputs(unique=False, resolve=False):
-    '''Return a list of mocked artifacts which contain sample which contain artifact ... Simple!'''
+    """Return a list of mocked artifacts which contain samples which contain artifacts ... Simple!"""
     return (
-        Mock(samples=[Mock(artifact=fake_artifact(id='a1'), id='s1', udf={"Proceed To SeqLab": True})]),
-        Mock(samples=[Mock(artifact=fake_artifact(id='a2'), id='s2', udf={"Proceed To SeqLab": True, "2D Barcode": 'fluidX1'})])
+        Mock(samples=[Mock(artifact=fake_artifact('a1'), id='s1', udf={'Proceed To SeqLab': True})]),
+        Mock(samples=[Mock(artifact=fake_artifact('a2'), id='s2', udf={'Proceed To SeqLab': True, '2D Barcode': 'fluidX1'})])
     )
 
 
@@ -42,4 +41,3 @@ class TestAssignWorkflowPreSeqLab(TestEPP):
             pws.assert_called_with(self.epp.lims, 'PreSeqLab EG 6.0', 'Sequencing Plate Preparation EG 2.0')
             assert sorted([a.id for a in self.epp.lims.route_artifacts.call_args[0][0]]) == ['a1', 'fx2']
             assert self.epp.lims.route_artifacts.call_args[1] == {'stage_uri': 'a_uri'}
-
diff --git a/tests/test_assign_to_workflow_receive_sample.py b/tests/test_assign_to_workflow_receive_sample.py
index e9a02e7..fd1492d 100644
--- a/tests/test_assign_to_workflow_receive_sample.py
+++ b/tests/test_assign_to_workflow_receive_sample.py
@@ -2,13 +2,15 @@ from scripts.assign_workflow_receive_sample import AssignWorkflowReceiveSample
 from tests.test_common import TestEPP
 from unittest.mock import Mock, patch, PropertyMock, call
 
+
 def fake_all_inputs(unique=False, resolve=False):
-    '''Return a list of mocked artifacts which contain sample with udf'''
+    """Return a list of mocked artifacts which contain samples with UDFs"""
     return (
         Mock(id='ai1', samples=[Mock(udf={'User Prepared Library': 'Yes'})]),
-        Mock(id='ai2', samples=[Mock(udf={})])
+        Mock(id='ai2', samples=[Mock(udf={'User Prepared Library': 'No'})])
     )
 
+
 class TestAssignWorkflowReceiveSample(TestEPP):
     def setUp(self):
         self.patched_process = patch.object(
@@ -33,8 +35,8 @@ class TestAssignWorkflowReceiveSample(TestEPP):
             self.epp._run()
 
             pws.assert_has_calls((
-                call(self.epp.lims, "User Prepared Library Batch EG1.0 WF", "User Prepared Library Batch EG 1.0 ST"),
-                call(self.epp.lims, "PreSeqLab EG 6.0", "Spectramax Picogreen EG 6.0")
+                call(self.epp.lims, 'User Prepared Library Batch EG1.0 WF', 'User Prepared Library Batch EG 1.0 ST'),
+                call(self.epp.lims, 'PreSeqLab EG 6.0', 'Spectramax Picogreen EG 6.0')
             ))
             # first routing (user prepared library)
             route_args = self.epp.lims.route_artifacts.call_args_list[0]
@@ -43,4 +45,4 @@ class TestAssignWorkflowReceiveSample(TestEPP):
 
             # second routing (not user prepared library)
             route_args = self.epp.lims.route_artifacts.call_args_list[1]
-            assert sorted([a.id for a in route_args[0][0]]) == ['ai2',]
+            assert sorted([a.id for a in route_args[0][0]]) == ['ai2']
diff --git a/tests/test_assign_to_workflow_seqlab_quantstudio.py b/tests/test_assign_to_workflow_seqlab_quantstudio.py
index 9676675..753b9d8 100644
--- a/tests/test_assign_to_workflow_seqlab_quantstudio.py
+++ b/tests/test_assign_to_workflow_seqlab_quantstudio.py
@@ -1,14 +1,14 @@
 from scripts.assign_workflow_seqlab_quantstudio import AssignWorkflowSeqLabQuantStudio
 from tests.test_common import TestEPP, fake_artifact
-from unittest.mock import Mock, patch, PropertyMock, MagicMock, call
+from unittest.mock import Mock, patch, PropertyMock, call
 
 
 def fake_all_output(unique=False, resolve=False):
-    '''Return a list of mocked artifacts which contain sample which contain artifact ... Simple!'''
+    """Return a list of mocked artifacts which contain samples which contain artifacts... Simple!"""
     return (
-        Mock(id='ao1', samples=[Mock(artifact=fake_artifact(id='a1'), id='s1', udf={"Prep Workflow": "TruSeq PCR-Free DNA Sample Prep", "Species": "Homo sapiens"})]),
-        Mock(id='ao2', samples=[Mock(artifact=fake_artifact(id='a2'), id='s2', udf={"Prep Workflow": "TruSeq Nano DNA Sample Prep"})]),
-        Mock(id='ao3', samples=[Mock(artifact=fake_artifact(id='a3'), id='s3', udf={"Prep Workflow": "TruSeq Nano DNA Sample Prep", "Species": "Homo sapiens", "2D Barcode": 'fluidX1'})])
+        Mock(id='ao1', samples=[Mock(artifact=fake_artifact('a1'), id='s1', udf={'Prep Workflow': 'TruSeq PCR-Free DNA Sample Prep', 'Species': 'Homo sapiens'})]),
+        Mock(id='ao2', samples=[Mock(artifact=fake_artifact('a2'), id='s2', udf={'Prep Workflow': 'TruSeq Nano DNA Sample Prep'})]),
+        Mock(id='ao3', samples=[Mock(artifact=fake_artifact('a3'), id='s3', udf={'Prep Workflow': 'TruSeq Nano DNA Sample Prep', 'Species': 'Homo sapiens', '2D Barcode': 'fluidX1'})])
     )
 
 
@@ -40,9 +40,9 @@ class TestAssignWorkflowSeqLabQuantStudio(TestEPP):
             self.epp._run()
 
             pws.assert_has_calls((
-                call(self.epp.lims, "TruSeq PCR-Free DNA Sample Prep", "Visual QC"),
-                call(self.epp.lims, "TruSeq Nano DNA Sample Prep", "Visual QC"),
-                call(self.epp.lims, "QuantStudio EG1.0", "QuantStudio Plate Preparation EG1.0"),
+                call(self.epp.lims, 'TruSeq PCR-Free DNA Sample Prep', 'Visual QC'),
+                call(self.epp.lims, 'TruSeq Nano DNA Sample Prep', 'Visual QC'),
+                call(self.epp.lims, 'QuantStudio EG1.0', 'QuantStudio Plate Preparation EG1.0'),
             ))
             # first routing (pcr free)
             route_args = self.epp.lims.route_artifacts.call_args_list[0]
@@ -56,4 +56,3 @@ class TestAssignWorkflowSeqLabQuantStudio(TestEPP):
             # third routing (quantstudio)
             route_args = self.epp.lims.route_artifacts.call_args_list[2]
             assert sorted([a.id for a in route_args[0][0]]) == ['a1', 'fx3']
-
diff --git a/tests/test_assign_to_workflow_stage.py b/tests/test_assign_to_workflow_stage.py
index 2a34bbf..c3b3817 100644
--- a/tests/test_assign_to_workflow_stage.py
+++ b/tests/test_assign_to_workflow_stage.py
@@ -6,9 +6,6 @@ from scripts import assign_to_workflow_stage
 
 class TestAsignWorkflowStage(TestEPP):
     def setUp(self):
-
-        self.patched_process = patch.object(StepEPP, 'process', new_callable=PropertyMock(return_value=Mock(all_inputs=fake_all_inputs)))
-        self.patched_lims = patch.object(StepEPP, 'lims', new_callable=PropertyMock)
         self.epp = assign_to_workflow_stage.AssignWorkflowStage(
             'http://server:8080/a_step_uri',
             'a_user',
@@ -30,18 +27,20 @@ class TestAsignWorkflowStage(TestEPP):
             only_once=True
         )
 
-    def test_assign(self):
-        with patch('scripts.assign_to_workflow_stage.get_workflow_stage', return_value=Mock(uri='a_uri')) as p, \
-             self.patched_lims, self.patched_process:
+    @patch.object(StepEPP, 'lims', new_callable=PropertyMock)
+    @patch('scripts.assign_to_workflow_stage.get_workflow_stage', return_value=Mock(uri='a_uri'))
+    def test_assign(self, mocked_workflow_stage, mocked_lims):
+        with patch.object(StepEPP, 'process', new_callable=PropertyMock(return_value=Mock(all_inputs=fake_all_inputs))):
             self.epp._run()
-            p.assert_called_with(self.epp.lims, self.epp.workflow_name, 'a_stage_name')
+            mocked_workflow_stage.assert_called_with(self.epp.lims, self.epp.workflow_name, 'a_stage_name')
             exp_artifacts = ['a1', 'a2']
 
             assert sorted([a.id for a in self.epp.lims.route_artifacts.call_args[0][0]]) == exp_artifacts
             assert self.epp.lims.route_artifacts.call_args[1] == {'stage_uri': 'a_uri'}
 
-        with patch('scripts.assign_to_workflow_stage.get_workflow_stage', return_value=Mock(uri='a_uri')) as p, \
-            self.patched_lims, self.patched_process:
+            mocked_workflow_stage.reset_mock()
+            mocked_lims.reset_mock()
+
             self.epp2._run()
-            p.assert_called_with(self.epp2.lims, self.epp2.workflow_name, 'a_stage_name')
+            mocked_workflow_stage.assert_called_with(self.epp2.lims, self.epp2.workflow_name, 'a_stage_name')
             assert self.epp2.lims.route_artifacts.call_count == 0
diff --git a/tests/test_assign_to_workflow_user_library.py b/tests/test_assign_to_workflow_user_library.py
index 232b9a9..fde1ff3 100644
--- a/tests/test_assign_to_workflow_user_library.py
+++ b/tests/test_assign_to_workflow_user_library.py
@@ -4,10 +4,10 @@ from unittest.mock import Mock, patch, PropertyMock, call
 
 
 def fake_all_output(unique=False, resolve=False):
-    '''Return a list of mocked artifacts which contain sample which contain artifact ... Simple!'''
+    """Return a list of mocked artifacts which contain samples which contain artifacts... Simple!"""
     return (
-        Mock(id='ao1', samples=[Mock(artifact=fake_artifact(id='a1'), id='s1', udf={"Prep Workflow": "TruSeq PCR-Free DNA Sample Prep"})]),
-        Mock(id='ao2', samples=[Mock(artifact=fake_artifact(id='a2'), id='s2', udf={"Prep Workflow": "TruSeq Nano DNA Sample Prep"})]),
+        Mock(id='ao1', samples=[Mock(artifact=fake_artifact('a1'), id='s1', udf={'Prep Workflow': 'TruSeq PCR-Free DNA Sample Prep'})]),
+        Mock(id='ao2', samples=[Mock(artifact=fake_artifact('a2'), id='s2', udf={'Prep Workflow': 'TruSeq Nano DNA Sample Prep'})]),
     )
 
 
@@ -36,8 +36,8 @@ class TestAssignWorkflowUserPreparedLibrary(TestEPP):
             self.epp._run()
 
             pws.assert_has_calls((
-                call(self.epp.lims, "TruSeq PCR-Free DNA Sample Prep", "SEMI-AUTOMATED - Make and Read qPCR Quant"),
-                call(self.epp.lims, "TruSeq Nano DNA Sample Prep", "SEMI-AUTOMATED - Make LQC & Caliper GX QC"),
+                call(self.epp.lims, 'TruSeq PCR-Free DNA Sample Prep', 'SEMI-AUTOMATED - Make and Read qPCR Quant'),
+                call(self.epp.lims, 'TruSeq Nano DNA Sample Prep', 'SEMI-AUTOMATED - Make LQC & Caliper GX QC'),
             ))
             # first routing (pcr free)
             route_args = self.epp.lims.route_artifacts.call_args_list[0]
diff --git a/tests/test_common.py b/tests/test_common.py
index 4a2ebee..43d6423 100644
--- a/tests/test_common.py
+++ b/tests/test_common.py
@@ -5,6 +5,7 @@ from unittest.mock import Mock, PropertyMock, patch
 import EPPs
 from EPPs.common import StepEPP, RestCommunicationEPP, find_newest_artifact_originating_from
 
+
 class NamedMock(Mock):
     @property
     def name(self):
@@ -15,18 +16,18 @@ class MockedSamples(NamedMock):
     project = NamedMock(real_name='10015AT')
 
 
-def fake_artifact(id):
+def fake_artifact(_id):
     return Mock(
-        id=str(id),
+        id=str(_id),
         workflow_stages_and_statuses=[(Mock(uri='a_uri'), 'COMPLETE', 'stage1')]
     )
 
 
 def fake_all_inputs(unique=False, resolve=False):
-    """Return a list of mocked artifacts which contain samples which contain artifacts ... Simple!"""
+    """Return a list of mocked artifacts which contain samples which contain artifacts... Simple!"""
     return (
-        Mock(samples=[Mock(artifact=fake_artifact(id='a1'), id='s1')]),
-        Mock(samples=[Mock(artifact=fake_artifact(id='a2'), id='s2')])
+        Mock(samples=[Mock(artifact=fake_artifact('a1'), id='s1')]),
+        Mock(samples=[Mock(artifact=fake_artifact('a2'), id='s2')])
     )
 
 
diff --git a/tests/test_convert_and_dispatch_genotypes.py b/tests/test_convert_and_dispatch_genotypes.py
index d743099..177010d 100644
--- a/tests/test_convert_and_dispatch_genotypes.py
+++ b/tests/test_convert_and_dispatch_genotypes.py
@@ -10,6 +10,7 @@ from scripts.convert_and_dispatch_genotypes import GenotypeConversion, UploadVcf
 def open_files(list_of_files):
     return [open(f) for f in list_of_files]
 
+
 class TestGenotypeConversion(TestCommon):
     test_records = {
         'id1': {'test_sample': '0/1', 'SNP': ['chr2', '120', 'id1', 'T', 'C', '.', '.', '.', 'GT']},
@@ -19,8 +20,9 @@ class TestGenotypeConversion(TestCommon):
     }
 
     def setUp(self):
-        self.geno_conversion = GenotypeConversion(open_files([self.genotype_csv]), self.accufill_log, 'igmm',
-                                                   self.small_reference_fai, flank_length=600)
+        self.geno_conversion = GenotypeConversion(
+            open_files([self.genotype_csv]), self.accufill_log, 'igmm',  self.small_reference_fai, flank_length=600
+        )
 
     def test_generate_vcf(self):
         # header_lines = ['##header line1', '##header line2']
@@ -91,14 +93,8 @@ class TestGenotypeConversion(TestCommon):
         assert find(valid_other_fieldnames, observed_fieldnames) == 'OTHER'
 
 
-
-
-
 class TestUploadVcfToSamples(TestEPP):
-
-
     def setUp(self):
-
         self.epp = UploadVcfToSamples(
             'http://server:8080/a_step_uri',
             'a_user',
diff --git a/tests/test_emails.py b/tests/test_emails.py
index d6d10aa..b77bc8f 100644
--- a/tests/test_emails.py
+++ b/tests/test_emails.py
@@ -1,9 +1,6 @@
 import os
 import platform
-
-import pytest
 from egcg_core.config import cfg
-
 from EPPs.common import SendMailEPP
 from scripts.email_data_release import DataReleaseEmail
 from scripts.email_data_trigger import DataReleaseEmailAndUpdateEPP
@@ -14,51 +11,42 @@ from unittest.mock import Mock, patch, PropertyMock
 
 
 class TestEmailEPP(TestEPP):
+    patch_project_multi = patch.object(
+        SendMailEPP,
+        'projects',
+        new_callable=PropertyMock(return_value=[NamedMock(real_name='project1'), NamedMock(real_name='project2')])
+    )
+
+    patch_project_single = patch.object(
+        SendMailEPP,
+        'projects',
+        new_callable=PropertyMock(return_value=[NamedMock(real_name='project1')])
+    )
+
+    patch_samples = patch.object(
+        SendMailEPP,
+        'samples',
+        new_callable=PropertyMock(return_value=[NamedMock(real_name='sample1'), NamedMock(real_name='sample2')])
+    )
+
+    patch_email = patch('egcg_core.notifications.email.send_email')
 
     def setUp(self):
         super().setUp()
         cfg.load_config_file(os.path.join(self.etc_path, 'example_clarity_script.yml'))
-
-        project1 = NamedMock(real_name='project1')
-        project2 = NamedMock(real_name='project2')
-
-        self.patch_project_multi = patch.object(
-            SendMailEPP,
-            'projects',
-            new_callable=PropertyMock(return_value=[project1, project2])
-        )
-
-        project1 = NamedMock(real_name='project1')
-        self.patch_project_single = patch.object(
-            SendMailEPP,
-            'projects',
-            new_callable=PropertyMock(return_value=[project1])
-        )
         self.patch_process = self.create_patch_process(SendMailEPP)
 
-        sample1 = NamedMock(real_name='sample1')
-        sample2 = NamedMock(real_name='sample2')
-        self.patch_samples = patch.object(
-            SendMailEPP,
-            'samples',
-            new_callable=PropertyMock(return_value=[sample1, sample2])
-        )
-
-        self.patch_email = patch('egcg_core.notifications.email.send_email')
-
-    def _test_only_one_project(self, epp):
-        with pytest.raises(ValueError):
-            with self.patch_project_multi:
-                epp._run()
+    def test_only_one_project(self):
+        try:
+            with self.assertRaises(ValueError):
+                with self.patch_project_multi:
+                    self.epp._run()
 
+        except NotImplementedError:
+            print('Skipping test for abstract class: ' + self.epp.__class__.__name__)
 
     def create_epp(self, klass):
-        return klass(
-            'http://server:8080/a_step_uri',
-            'a_user',
-            'a_password',
-            self.log_file
-        )
+        return klass('http://server:8080/a_step_uri', 'a_user', 'a_password', self.log_file)
 
     @staticmethod
     def create_patch_process(klass, udfs=None):
@@ -73,17 +61,18 @@ class TestDataReleaseEmailAndUpdateEPP(TestEmailEPP):
     def setUp(self):
         super().setUp()
         self.epp = self.create_epp(DataReleaseEmailAndUpdateEPP)
-        self.patch_process = self.create_patch_process(DataReleaseEmailAndUpdateEPP, {
+        self.patch_process = self.create_patch_process(
+            DataReleaseEmailAndUpdateEPP,
+            {
                 'Data Download Contact Name 1': 'John Doe',
                 'Data Download Contact Name 2': 'Jane Doe',
                 'Is Contact 1 A New or Existing User?': 'New User',
                 'Is Contact 2 A New or Existing User?': 'Existing User'
-            })
+            }
+        )
 
     def test_send_email(self):
-        self._test_only_one_project(self.epp)
-
-        with self.patch_project_single, self.patch_process, self.patch_samples, self.patch_email as mocked_send_email:
+        with self.patch_project_single, self.patch_process, self.patch_samples, self.patch_email as mocked_email:
             self.epp._run()
             msg = '''Hi Bioinformatics,
 
@@ -99,7 +88,7 @@ Jane Doe (Existing User)
 Kind regards,
 ClarityX'''
             msg = msg.format(localmachine=platform.node())
-            mocked_send_email.assert_called_with(
+            mocked_email.assert_called_with(
                 msg=msg,
                 subject='project1: Please release data',
                 mailhost='smtp.test.me',
@@ -114,13 +103,17 @@ class TestDataReleaseEmail(TestEmailEPP):
     def setUp(self):
         super().setUp()
         self.epp = self.create_epp(DataReleaseEmail)
-        self.patch_process1 = self.create_patch_process(DataReleaseEmail, {'Request Customer Survey (Final Data Release)': 'No'})
-        self.patch_process2 = self.create_patch_process(DataReleaseEmail, {'Request Customer Survey (Final Data Release)': 'Yes'})
+        self.patch_process1 = self.create_patch_process(
+            DataReleaseEmail,
+            {'Request Customer Survey (Final Data Release)': 'No'}
+        )
+        self.patch_process2 = self.create_patch_process(
+            DataReleaseEmail,
+            {'Request Customer Survey (Final Data Release)': 'Yes'}
+        )
 
     def test_send_email(self):
-        self._test_only_one_project(self.epp)
-
-        with self.patch_project_single, self.patch_process1, self.patch_samples, self.patch_email as mocked_send_email:
+        with self.patch_project_single, self.patch_process1, self.patch_samples, self.patch_email as mocked_email:
             self.epp._run()
             msg = '''Hi,
 
@@ -132,7 +125,7 @@ Kind regards,
 ClarityX'''
             msg = msg.format(localmachine=platform.node())
 
-            mocked_send_email.assert_called_with(
+            mocked_email.assert_called_with(
                 msg=msg,
                 subject='project1: Data Released',
                 mailhost='smtp.test.me',
@@ -170,8 +163,6 @@ class TestFluidXSampleReceiptEmail(TestEmailEPP):
         self.epp = self.create_epp(FluidXSampleReceiptEmail)
 
     def test_send_email(self):
-        self._test_only_one_project(self.epp)
-
         with self.patch_project_single, self.patch_process, self.patch_samples, self.patch_email as mocked_send_email:
             self.epp._run()
             msg = '''Hi,
@@ -215,15 +206,14 @@ ClarityX'''
                 strict=True
             )
 
+
 class TestReceiveSampleEmail(TestEmailEPP):
     def setUp(self):
         super().setUp()
         self.epp = self.create_epp(ReceiveSampleEmail)
 
     def test_send_email(self):
-        self._test_only_one_project(self.epp)
-
-        with self.patch_project_single, self.patch_process, self.patch_samples, self.patch_email as mocked_send_email:
+        with self.patch_project_single, self.patch_process, self.patch_samples, self.patch_email as mocked_email:
             self.epp._run()
             msg = '''Hi,
 
@@ -234,8 +224,7 @@ https://{localmachine}/clarity/work-details/tep_uri
 Kind regards,
 ClarityX'''
             msg = msg.format(localmachine=platform.node())
-
-            mocked_send_email.assert_called_with(
+            mocked_email.assert_called_with(
                 msg=msg,
                 subject='project1: Plate Received',
                 mailhost='smtp.test.me',

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
asana==0.6.5
attrs==22.2.0
cached-property==1.5.2
certifi==2021.5.30
-e git+https://github.com/EdinburghGenomics/clarity_scripts.git@37d6a75d81d94652c5c29228f86954b0ea96f8bb#egg=clarity_scripts
EGCG-Core==0.7.3
importlib-metadata==4.8.3
iniconfig==1.1.1
Jinja2==2.8
MarkupSafe==2.0.1
oauthlib==3.2.2
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyclarity-lims==0.4.8
pyparsing==3.1.4
pytest==7.0.1
PyYAML==6.0.1
requests==2.14.2
requests-oauthlib==0.6.2
six==1.10.0
tomli==1.2.3
typing_extensions==4.1.1
zipp==3.6.0

name: clarity_scripts
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - asana==0.6.5
      - attrs==22.2.0
      - cached-property==1.5.2
      - egcg-core==0.7.3
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - jinja2==2.8
      - markupsafe==2.0.1
      - oauthlib==3.2.2
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pyclarity-lims==0.4.8
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pyyaml==6.0.1
      - requests==2.14.2
      - requests-oauthlib==0.6.2
      - six==1.10.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - zipp==3.6.0
prefix: /opt/conda/envs/clarity_scripts

tests/test_assign_container_name.py::TestAssignContainerName::test_findAvailableContainer tests/test_assign_to_workflow_receive_sample.py::TestAssignWorkflowReceiveSample::test_assign
tests/test_assign_container_name.py::TestEPP::test_init tests/test_assign_container_name.py::TestAssignContainerName::test_assign_existing_arts tests/test_assign_container_name.py::TestAssignContainerName::test_assign_no_arts tests/test_assign_container_name.py::TestAssignContainerName::test_init tests/test_assign_to_workflow_preseqlab.py::TestEPP::test_init tests/test_assign_to_workflow_preseqlab.py::TestAssignWorkflowPreSeqLab::test_assign tests/test_assign_to_workflow_preseqlab.py::TestAssignWorkflowPreSeqLab::test_init tests/test_assign_to_workflow_receive_sample.py::TestEPP::test_init tests/test_assign_to_workflow_receive_sample.py::TestAssignWorkflowReceiveSample::test_init tests/test_assign_to_workflow_seqlab_quantstudio.py::TestEPP::test_init tests/test_assign_to_workflow_seqlab_quantstudio.py::TestAssignWorkflowSeqLabQuantStudio::test_assign tests/test_assign_to_workflow_seqlab_quantstudio.py::TestAssignWorkflowSeqLabQuantStudio::test_init tests/test_assign_to_workflow_stage.py::TestEPP::test_init tests/test_assign_to_workflow_stage.py::TestAsignWorkflowStage::test_assign tests/test_assign_to_workflow_stage.py::TestAsignWorkflowStage::test_init tests/test_assign_to_workflow_user_library.py::TestEPP::test_init tests/test_assign_to_workflow_user_library.py::TestAssignWorkflowUserPreparedLibrary::test_assign tests/test_assign_to_workflow_user_library.py::TestAssignWorkflowUserPreparedLibrary::test_init tests/test_common.py::TestEPP::test_init tests/test_common.py::TestRestCommunicationEPP::test_interaction tests/test_common.py::TestFindNewestArtifactOriginatingFrom::test_find_newest_artifact_originating_from tests/test_convert_and_dispatch_genotypes.py::TestEPP::test_init tests/test_convert_and_dispatch_genotypes.py::TestGenotypeConversion::test_find_field tests/test_convert_and_dispatch_genotypes.py::TestGenotypeConversion::test_generate_vcf tests/test_convert_and_dispatch_genotypes.py::TestGenotypeConversion::test_get_genotype_from_call tests/test_convert_and_dispatch_genotypes.py::TestGenotypeConversion::test_init_genotype_csv tests/test_convert_and_dispatch_genotypes.py::TestGenotypeConversion::test_order_from_fai tests/test_convert_and_dispatch_genotypes.py::TestGenotypeConversion::test_parse_QuantStudio_AIF_genotype tests/test_convert_and_dispatch_genotypes.py::TestGenotypeConversion::test_parse_genome_fai tests/test_convert_and_dispatch_genotypes.py::TestGenotypeConversion::test_vcf_header_from_ref_length tests/test_convert_and_dispatch_genotypes.py::TestUploadVcfToSamples::test_init tests/test_convert_and_dispatch_genotypes.py::TestUploadVcfToSamples::test_upload tests/test_emails.py::TestEPP::test_init tests/test_emails.py::TestEmailEPP::test_init tests/test_emails.py::TestEmailEPP::test_only_one_project tests/test_emails.py::TestDataReleaseEmailAndUpdateEPP::test_init tests/test_emails.py::TestDataReleaseEmailAndUpdateEPP::test_only_one_project tests/test_emails.py::TestDataReleaseEmailAndUpdateEPP::test_send_email tests/test_emails.py::TestDataReleaseEmail::test_init tests/test_emails.py::TestDataReleaseEmail::test_only_one_project tests/test_emails.py::TestDataReleaseEmail::test_send_email tests/test_emails.py::TestFluidXSampleReceiptEmail::test_init tests/test_emails.py::TestFluidXSampleReceiptEmail::test_only_one_project tests/test_emails.py::TestFluidXSampleReceiptEmail::test_send_email tests/test_emails.py::TestReceiveSampleEmail::test_init tests/test_emails.py::TestReceiveSampleEmail::test_only_one_project tests/test_emails.py::TestReceiveSampleEmail::test_send_email
scripts/assign_workflow_user_library.py scripts/prepare_discard_plate.py scripts/email_fluidx_sample_receipt.py scripts/email_receive_sample.py scripts/assign_workflow_receive_sample.py scripts/assign_container_name.py scripts/convert_and_dispatch_genotypes.py scripts/create_reagents_for_run.py scripts/assign_workflow_seqlab_quantstudio.py scripts/email_data_trigger.py requirements.txt scripts/assign_workflow_preseqlab.py scripts/email_data_release.py
scripts/assign_workflow_user_library.py scripts/prepare_discard_plate.py scripts/email_fluidx_sample_receipt.py scripts/email_receive_sample.py scripts/assign_workflow_receive_sample.py scripts/assign_container_name.py scripts/convert_and_dispatch_genotypes.py scripts/create_reagents_for_run.py scripts/assign_workflow_seqlab_quantstudio.py scripts/email_data_trigger.py requirements.txt scripts/assign_workflow_preseqlab.py scripts/email_data_release.py