Repository: cnr-ibba/IMAGE-ValidationTool

Issue:
update IMAGE-metadata (a215d2a) requires additional fixes to work properly
Last version of IMAGE-Metadata [a215d2a](https://github.com/cnr-ibba/IMAGE-metadata/commit/a215d2a1e8b772fc46560947dbabdfcbcd686d21) require additional fixes in ruleset and changes in test_data of IMAGE_Validation tool. Python-package branch #2 implement additional test to old image data, which don't pass at this moment ([Build #77](https://travis-ci.org/cnr-ibba/IMAGE-ValidationTool/builds/515638279)). First one:
```python
from image_validation import validation, ValidationResult, static_parameters
ruleset = validation.read_in_ruleset(static_parameters.ruleset_filename)
validation.check_ruleset(ruleset)
['Error: there is no allowed values for field Alternative id being of limited value type',
 'Error: No valid values should be provided to field Project as being of text type',
 'Warning: ontology terms are provided for field Person role. Please re-consider whether it needs to change to ontology_id.',
 'Error: there is no allowed values for field Gene bank country being of limited value type',
 'Error: No valid values should be provided to field Organization country as being of text type',
 'Warning: ontology terms are provided for field Organization country. Please re-consider whether it needs to change to ontology_id.',
 'Error: No valid values should be provided to field Availability as being of text type',
 'Error: No valid values should be provided to field Sample storage as being of text type',
 'Error: No valid values should be provided to field Sample storage processing as being of text type']
```
Second one is related to the changed *Material* attribute from `organism` to `Organism` and `specimen from organism` to `Specimen from Organism`. For example:
```python
animal = json.load(open("test_data/image_animal.json"))
ruleset.validate(animal).get_messages()
['Warning: Ontology provided for field Gene bank country however there is no requirement in the ruleset (standard section) for Record ANIMAL:::ID:::CS05_1999',
 'Warning: Column Birth location accuracy could not be found in ruleset for Record ANIMAL:::ID:::CS05_1999',
 'Warning: Column EFABIS Breed country could not be found in ruleset for Record ANIMAL:::ID:::CS05_1999',
 'Warning: Column Sex could not be found in ruleset for Record ANIMAL:::ID:::CS05_1999',
 'Warning: Column Supplied breed could not be found in ruleset for Record ANIMAL:::ID:::CS05_1999']
```
Does't find the correct section in metadata ruleset, and mark all attributes as addictiional and not required from image (without asserting anything on such values) By changing `Material` with the new specification as following:
```python
animal['attributes']['Material'][0]['value'] = 'Organism'
ruleset.validate(animal).get_messages()
['Warning: Provided value Organism has different letter case to the term referenced by http://purl.obolibrary.org/obo/OBI_0100026 (standard section) for Record ANIMAL:::ID:::CS05_1999',
 'Warning: Ontology provided for field Gene bank country however there is no requirement in the ruleset (standard section) for Record ANIMAL:::ID:::CS05_1999']
```
Validation seems to apply correctly. However, `Organism` now raises warnings since is in uppercase, and ontology term for genebank country was removed in last ruleset. Same considerations could be done for samples. 

Discussion:

Patch:
diff --git a/MANIFEST.in b/MANIFEST.in
index 3a2041c..d444141 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -3,7 +3,7 @@ include CONTRIBUTING.rst
 include HISTORY.rst
 include LICENSE
 include README.rst
-include image_validation/sample_ruleset_v1.4.json
+include image_validation/sample_ruleset_v1.5.json
 
 recursive-include tests *
 recursive-exclude * __pycache__
diff --git a/TODO.rst b/TODO.rst
new file mode 100644
index 0000000..9254fa8
--- /dev/null
+++ b/TODO.rst
@@ -0,0 +1,6 @@
+
+======
+ TODO
+======
+
+* Change ``scripts/main.py`` in a CLI script installed through the module
diff --git a/image_validation/Ruleset.py b/image_validation/Ruleset.py
index 7f6181e..5e82012 100644
--- a/image_validation/Ruleset.py
+++ b/image_validation/Ruleset.py
@@ -1,11 +1,10 @@
 import logging
+import json
 from typing import List, Dict
 
 from . import misc
 from . import ValidationResult
-from . import use_ontology
 from . import static_parameters
-from . import validation
 
 logger = logging.getLogger(__name__)
 
@@ -331,6 +330,11 @@ class RuleSection:
         # rules are organized by requirement first, so this serves as a shortcut
         self.rule_names: Dict[str, int] = {}
 
+    def toJSON(self):
+        return json.dumps(
+                self, default=lambda o: o.__dict__,
+                sort_keys=True, indent=2)
+
     def add_rule(self, rule: RuleField):
         if type(rule) is not RuleField:
             raise TypeError("The rule parameter must be a RuleField")
@@ -380,17 +384,28 @@ class RuleSection:
             raise TypeError("The parameter record must hold a Dict type")
         if 'attributes' not in record:
             raise KeyError("No attributes existing in the record")
+
         conditions = self.get_conditions()
         attributes = record['attributes']
+
         for field_name in conditions.keys():
             if field_name not in attributes:
+                logger.debug("%s not in attributes" % (field_name))
                 return False
+
+            else:
+                logger.debug("found %s in %s" % (field_name, conditions))
+
             actual_values = attributes[field_name]
+            logger.debug("actual_values is %s" % actual_values)
+
             found = False
+
             for value in actual_values:
                 if value['value'] == conditions[field_name]:
                     found = True
                     break
+
             if not found:
                 return False
         return True
@@ -429,6 +444,11 @@ class RuleSet:
     def __init__(self):
         self.rule_sections: Dict[str, RuleSection] = {}
 
+    def toJSON(self):
+        return json.dumps(
+                self, default=lambda o: o.__dict__,
+                sort_keys=True, indent=2)
+
     def add_rule_section(self, rule_section: RuleSection):
         if type(rule_section) is not RuleSection:
             raise TypeError("The rule section parameter must be a RuleSection object")
@@ -451,6 +471,9 @@ class RuleSet:
         return self.rule_sections.get(section_name)
 
     def validate(self, record: Dict, id_field: str = 'Data source ID') -> ValidationResult.ValidationResultRecord:
+        logger.debug(
+            "got record: {record}, id_field: {id_field}".format(
+                    record=record, id_field=id_field))
         attributes = record['attributes']
         record_id = attributes[id_field][0]['value']
         record_result = ValidationResult.ValidationResultRecord(record_id)
@@ -458,7 +481,9 @@ class RuleSet:
         unmapped = attributes.copy()  # create a copy and remove the ruleset-mapped columns
         del unmapped[id_field]
         for section_name in self.get_all_section_names():
+            logger.debug("Processing section_name: %s" % (section_name))
             section_rule = self.get_section_by_name(section_name)
+            # logger.debug("Got section_rule: %s" % (section_rule.toJSON()))
             if section_rule.meet_condition(record):
                 logger.debug("Applying "+section_name+" ruleset to record "+record_id)
                 section_results = section_rule.validate(attributes, record_id, id_field)
@@ -468,10 +493,24 @@ class RuleSet:
                 for field_name in section_rule.get_rule_names():
                     if field_name in unmapped:
                         del unmapped[field_name]
-        # unmapped column check can only be done here, not in section rule validation as all section rules need to apply
+
+            else:
+                logger.debug(
+                    "section_rule %s doesn't meet_condition" % section_name)
+
+        # unmapped column check can only be done here, not in section rule
+        # validation as all section rules need to apply
         if unmapped:
+            logger.debug("found those unmapped keys: %s" % (unmapped.keys()))
+
             for key in unmapped.keys():
                 record_result.add_validation_result_column(
                     ValidationResult.ValidationResultColumn(
-                        "Warning", "Column " + key + " could not be found in ruleset", record_id))
+                        "Warning",
+                        "Column " + key + " could not be found in ruleset",
+                        record_id))
+
+        else:
+            logger.debug("No unmapped columns left")
+
         return record_result
diff --git a/image_validation/__init__.py b/image_validation/__init__.py
index f406beb..00d38c2 100644
--- a/image_validation/__init__.py
+++ b/image_validation/__init__.py
@@ -3,7 +3,7 @@ IMAGE ruleset represented as JSON"""
 
 __author__ = """Jun Fan"""
 __email__ = 'junf@ebi.ac.uk'
-__version__ = '1.1.0'
+__version__ = '1.2.0'
 
 from . import misc
 from . import use_ontology
diff --git a/image_validation/sample_ruleset_v1.4.json b/image_validation/sample_ruleset_v1.5.json
similarity index 86%
rename from image_validation/sample_ruleset_v1.4.json
rename to image_validation/sample_ruleset_v1.5.json
index 23dc531..5563429 100644
--- a/image_validation/sample_ruleset_v1.4.json
+++ b/image_validation/sample_ruleset_v1.5.json
@@ -1,468 +1,462 @@
 {
-   "name" : "IMAGE sample metadata rules",
+   "description" : "Validation rules for the IMAGE project.",
    "rule_groups" : [
       {
-         "name" : "standard",
          "rules" : [
             {
                "Required" : "mandatory",
-               "Name" : "Data source ID",
+               "Description" : "the id used in the original data source to identify the record",
                "Allow Multiple" : "no",
                "Example" : "SAMEA4450075, ANIMAL:::ID:::132713,SAMPLE:::ID:::19483",
-               "Description" : "the id used in the original data source to identify the record",
-               "Type" : "text"
+               "Type" : "text",
+               "Name" : "Data source ID"
             },
             {
-               "Type" : "text",
                "Description" : "the other identifier used in the orignal data source, e.g. accession, secondary id etc.",
+               "Required" : "mandatory",
+               "Type" : "text",
                "Example" : "Siems_0722_393449",
                "Allow Multiple" : "no",
-               "Name" : "Alternative id",
-               "Required" : "mandatory"
+               "Name" : "Alternative id"
             },
             {
-               "Required" : "optional",
                "Name" : "Description",
-               "Allow Multiple" : "no",
+               "Example" : "Pig sperm sample from 3 year old duroc",
                "Type" : "text",
+               "Allow Multiple" : "no",
                "Description" : "A brief description of the sample",
-               "Example" : "Pig sperm sample from 3 year old duroc"
+               "Required" : "optional"
             },
             {
-               "Type" : "limited value",
+               "Required" : "mandatory",
                "Description" : "All samples are stated to be part of the IMAGE project, while still belonging to each individual gene bank. This tag is required to identify them in public databases",
-               "Example" : "IMAGE",
-               "Allow Multiple" : "no",
                "Valid values" : [
                   "IMAGE"
                ],
                "Name" : "Project",
-               "Required" : "mandatory"
+               "Allow Multiple" : "no",
+               "Type" : "limited value",
+               "Example" : "IMAGE"
             },
             {
+               "Required" : "mandatory",
                "Description" : "A descriptive title for the submission",
-               "Example" : "German National Sperm Bank",
-               "Type" : "text",
-               "Allow Multiple" : "no",
                "Name" : "Submission title",
-               "Required" : "mandatory"
+               "Allow Multiple" : "no",
+               "Example" : "German National Sperm Bank",
+               "Type" : "text"
             },
             {
-               "Name" : "Submission description",
-               "Required" : "recommended",
+               "Type" : "text",
                "Example" : "A collection of cattle samples from local breeding stocks",
+               "Allow Multiple" : "no",
+               "Name" : "Submission description",
                "Description" : "A brief description of the submission",
-               "Type" : "text",
-               "Allow Multiple" : "no"
+               "Required" : "recommended"
             },
             {
+               "Name" : "Material",
                "Allow Multiple" : "no",
+               "Type" : "ontology_id",
                "Example" : "specimen from organism",
                "Description" : "type of the sample, either animal(organism) or sample (specimen from organism)",
-               "Required" : "mandatory",
-               "Name" : "Material",
                "Valid terms" : [
                   {
-                     "allow_descendants" : 0,
-                     "label" : "organism",
-                     "ontology_name" : "OBI",
                      "include_root" : 1,
-                     "term" : "OBI_0100026"
+                     "label" : "organism",
+                     "allow_descendants" : 0,
+                     "term" : "OBI_0100026",
+                     "ontology_name" : "OBI"
                   },
                   {
                      "allow_descendants" : 0,
-                     "term" : "OBI_0001479",
-                     "ontology_name" : "OBI",
                      "label" : "specimen from organism",
-                     "include_root" : 1
+                     "include_root" : 1,
+                     "ontology_name" : "OBI",
+                     "term" : "OBI_0001479"
                   }
                ],
-               "Type" : "ontology_id"
+               "Required" : "mandatory"
             },
             {
                "Name" : "Person last name",
-               "Required" : "mandatory",
                "Example" : "Harrison",
-               "Description" : "the last name of the submitter or contact for these samples, multiple persons can be provided",
                "Type" : "text",
-               "Allow Multiple" : "yes"
+               "Allow Multiple" : "yes",
+               "Description" : "the last name of the submitter or contact for these samples, multiple persons can be provided",
+               "Required" : "mandatory"
             },
             {
-               "Name" : "Person initial",
-               "Required" : "optional",
+               "Example" : "W",
                "Type" : "text",
+               "Allow Multiple" : "yes",
+               "Name" : "Person initial",
                "Description" : "the initial of the submitter or contact for these samples, multiple persons can be provided",
-               "Example" : "W",
-               "Allow Multiple" : "yes"
+               "Required" : "optional"
             },
             {
-               "Required" : "recommended",
                "Name" : "Person first name",
-               "Description" : "the first name of the submitter or contact for these samples, multiple persons can be provided",
-               "Type" : "text",
                "Example" : "Peter",
-               "Allow Multiple" : "yes"
+               "Type" : "text",
+               "Allow Multiple" : "yes",
+               "Description" : "the first name of the submitter or contact for these samples, multiple persons can be provided",
+               "Required" : "recommended"
             },
             {
+               "Description" : "email of contact person",
                "Required" : "mandatory",
                "Name" : "Person email",
-               "Allow Multiple" : "yes",
                "Type" : "uri",
-               "Description" : "email of contact person",
-               "Example" : "mailto:peter@ebi.ac.uk"
+               "Example" : "mailto:peter@ebi.ac.uk",
+               "Allow Multiple" : "yes"
             },
             {
                "Description" : "affiliation of person",
-               "Example" : "EMBL-EBI",
+               "Required" : "mandatory",
                "Type" : "text",
+               "Example" : "EMBL-EBI",
                "Allow Multiple" : "yes",
-               "Name" : "Person affiliation",
-               "Required" : "mandatory"
+               "Name" : "Person affiliation"
             },
             {
+               "Name" : "Person role",
+               "Example" : "submitter",
+               "Type" : "ontology_id",
                "Allow Multiple" : "yes",
                "Valid terms" : [
                   {
-                     "allow_descendants" : 1,
-                     "include_root" : 0,
+                     "term" : "EFO_0002012",
                      "ontology_name" : "EFO",
-                     "term" : "EFO_0002012"
+                     "include_root" : 0,
+                     "allow_descendants" : 1
                   }
                ],
                "Description" : "the role (type of the contribution) made by the person",
-               "Example" : "submitter",
-               "Type" : "ontology_id",
-               "Name" : "Person role",
                "Required" : "mandatory"
             },
             {
-               "Required" : "mandatory",
-               "Name" : "Organization name",
-               "Description" : "Organization name, multiple can be supplied, but at least one must be the name of the gene bank",
                "Type" : "text",
                "Example" : "EMBL-EBI",
-               "Allow Multiple" : "yes"
+               "Allow Multiple" : "yes",
+               "Name" : "Organization name",
+               "Description" : "Organization name, multiple can be supplied, but at least one must be the name of the gene bank",
+               "Required" : "mandatory"
             },
             {
                "Name" : "Organization address",
-               "Required" : "recommended",
-               "Description" : "The address of the organisation, multiple can be supplied",
                "Example" : "European Bioinformatics Institute, Wellcome Trust Genome Campus, CB10 1SD, UK",
                "Type" : "text",
-               "Allow Multiple" : "yes"
+               "Allow Multiple" : "yes",
+               "Description" : "The address of the organisation, multiple can be supplied",
+               "Required" : "recommended"
             },
             {
-               "Type" : "uri",
-               "Description" : "the websites of orgnization, multiple can be supplied",
+               "Name" : "Organization uri",
                "Example" : "www.ebi.ac.uk",
+               "Type" : "uri",
                "Allow Multiple" : "yes",
-               "Name" : "Organization uri",
+               "Description" : "the websites of orgnization, multiple can be supplied",
                "Required" : "optional"
             },
             {
+               "Name" : "Organization country",
+               "Allow Multiple" : "yes",
+               "Type" : "ontology_id",
+               "Example" : "United Kingdom",
+               "Required" : "recommended",
                "Valid terms" : [
                   {
-                     "allow_descendants" : 0,
                      "term" : "NCIT_C16592",
-                     "include_root" : 1,
                      "ontology_name" : "NCIT",
-                     "label" : "France"
+                     "include_root" : 1,
+                     "label" : "France",
+                     "allow_descendants" : 0
                   },
                   {
+                     "label" : "Egypt",
                      "allow_descendants" : 0,
-                     "term" : "NCIT_C16530",
+                     "include_root" : 1,
                      "ontology_name" : "NCIT",
-                     "label" : "Egypt",
-                     "include_root" : 1
+                     "term" : "NCIT_C16530"
                   },
                   {
-                     "term" : "NCIT_C16449",
                      "include_root" : 1,
-                     "ontology_name" : "NCIT",
                      "label" : "Colombia",
-                     "allow_descendants" : 0
+                     "allow_descendants" : 0,
+                     "term" : "NCIT_C16449",
+                     "ontology_name" : "NCIT"
                   },
                   {
-                     "allow_descendants" : 0,
                      "term" : "NCIT_C17181",
                      "ontology_name" : "NCIT",
                      "include_root" : 1,
-                     "label" : "Switzerland"
+                     "label" : "Switzerland",
+                     "allow_descendants" : 0
                   },
                   {
-                     "term" : "NCIT_C16903",
+                     "allow_descendants" : 0,
+                     "label" : "Netherlands",
                      "include_root" : 1,
                      "ontology_name" : "NCIT",
-                     "label" : "Netherlands",
-                     "allow_descendants" : 0
+                     "term" : "NCIT_C16903"
                   },
                   {
                      "allow_descendants" : 0,
                      "label" : "Germany",
-                     "ontology_name" : "NCIT",
                      "include_root" : 1,
+                     "ontology_name" : "NCIT",
                      "term" : "NCIT_C16636"
                   },
                   {
-                     "allow_descendants" : 0,
                      "term" : "NCIT_C16761",
                      "ontology_name" : "NCIT",
-                     "label" : "Italy",
-                     "include_root" : 1
+                     "include_root" : 1,
+                     "allow_descendants" : 0,
+                     "label" : "Italy"
                   },
                   {
-                     "allow_descendants" : 0,
                      "ontology_name" : "NCIT",
+                     "term" : "NCIT_C16699",
+                     "allow_descendants" : 0,
                      "label" : "Hungary",
-                     "include_root" : 1,
-                     "term" : "NCIT_C16699"
+                     "include_root" : 1
                   },
                   {
-                     "allow_descendants" : 0,
-                     "include_root" : 1,
                      "ontology_name" : "NCIT",
+                     "term" : "NCIT_C16878",
                      "label" : "Morocco",
-                     "term" : "NCIT_C16878"
+                     "allow_descendants" : 0,
+                     "include_root" : 1
                   },
                   {
                      "allow_descendants" : 0,
+                     "label" : "Spain",
                      "include_root" : 1,
                      "ontology_name" : "NCIT",
-                     "label" : "Spain",
                      "term" : "NCIT_C17152"
                   },
                   {
+                     "include_root" : 1,
                      "allow_descendants" : 0,
-                     "term" : "NCIT_C16305",
                      "label" : "Argentina",
-                     "ontology_name" : "NCIT",
-                     "include_root" : 1
+                     "term" : "NCIT_C16305",
+                     "ontology_name" : "NCIT"
                   },
                   {
+                     "term" : "NCIT_C17180",
                      "ontology_name" : "NCIT",
                      "include_root" : 1,
                      "label" : "Sweden",
-                     "term" : "NCIT_C17180",
                      "allow_descendants" : 0
                   },
                   {
                      "term" : "NCIT_C17233",
                      "ontology_name" : "NCIT",
                      "include_root" : 1,
-                     "label" : "United Kingdom",
-                     "allow_descendants" : 0
+                     "allow_descendants" : 0,
+                     "label" : "United Kingdom"
                   },
                   {
                      "include_root" : 1,
-                     "ontology_name" : "NCIT",
+                     "allow_descendants" : 0,
                      "label" : "Poland",
                      "term" : "NCIT_C17002",
-                     "allow_descendants" : 0
+                     "ontology_name" : "NCIT"
                   },
                   {
-                     "term" : "NCIT_C17006",
-                     "include_root" : 1,
                      "ontology_name" : "NCIT",
+                     "term" : "NCIT_C17006",
+                     "allow_descendants" : 0,
                      "label" : "Portugal",
-                     "allow_descendants" : 0
+                     "include_root" : 1
                   },
                   {
                      "term" : "NCIT_C16312",
                      "ontology_name" : "NCIT",
-                     "label" : "Austria",
                      "include_root" : 1,
+                     "label" : "Austria",
                      "allow_descendants" : 0
                   }
                ],
-               "Type" : "ontology_id",
-               "Required" : "recommended",
-               "Name" : "Organization country",
-               "Allow Multiple" : "yes",
-               "Example" : "United Kingdom",
                "Description" : "Country in which organization resides, the organization list is maintained at https://github.com/cnr-ibba/IMAGE-InjectTool/blob/devel/django-data/image/image_app/management/commands/organization_list.csv"
             },
             {
-               "Description" : "the role of the organization, e.g. biobank, funder",
-               "Example" : "submitter",
+               "Name" : "Organization role",
                "Type" : "ontology_id",
+               "Example" : "submitter",
                "Allow Multiple" : "yes",
+               "Description" : "the role of the organization, e.g. biobank, funder",
                "Valid terms" : [
                   {
-                     "allow_descendants" : 1,
-                     "include_root" : 0,
+                     "term" : "EFO_0002012",
                      "ontology_name" : "EFO",
-                     "term" : "EFO_0002012"
+                     "include_root" : 0,
+                     "allow_descendants" : 1
                   }
                ],
-               "Name" : "Organization role",
                "Required" : "mandatory"
             },
             {
+               "Required" : "optional",
                "Description" : "Publication associated with sample, supplied as the digital object identifier which can unambiguously point to the publication",
-               "Example" : "doi:10.1139/o09-005",
-               "Type" : "doi",
+               "Name" : "Publication DOI",
                "Allow Multiple" : "no",
-               "Required" : "optional",
-               "Name" : "Publication DOI"
+               "Type" : "doi",
+               "Example" : "10.1139/o09-005"
             },
             {
+               "Description" : "The name of the gene bank that the data of this submission belongs to, ideally use the value stored in EUGENA",
                "Required" : "mandatory",
                "Name" : "Gene bank name",
-               "Allow Multiple" : "no",
-               "Description" : "The name of the gene bank that the data of this submission belongs to, ideally use the value stored in EUGENA",
                "Type" : "text",
-               "Example" : "CryoWeb"
+               "Example" : "CryoWeb",
+               "Allow Multiple" : "no"
             },
             {
-               "Type" : "ontology_id",
+               "Allow Multiple" : "no",
+               "Name" : "Gene bank country",
+               "Required" : "mandatory",
+               "Description" : "Country in which the gene bank resides, particularly important when different countries use the same database infrastructure, e.g. CryoWeb. The controlled vocabulary for countries is from INSDC http://www.insdc.org/documents/country-qualifier-vocabulary",
                "Valid terms" : [
                   {
-                     "allow_descendants" : 0,
-                     "ontology_name" : "NCIT",
                      "include_root" : 1,
                      "label" : "France",
-                     "term" : "NCIT_C16592"
+                     "allow_descendants" : 0,
+                     "term" : "NCIT_C16592",
+                     "ontology_name" : "NCIT"
                   },
                   {
-                     "allow_descendants" : 0,
                      "term" : "NCIT_C16530",
-                     "label" : "Egypt",
                      "ontology_name" : "NCIT",
-                     "include_root" : 1
+                     "include_root" : 1,
+                     "allow_descendants" : 0,
+                     "label" : "Egypt"
                   },
                   {
                      "term" : "NCIT_C16449",
-                     "label" : "Colombia",
                      "ontology_name" : "NCIT",
                      "include_root" : 1,
-                     "allow_descendants" : 0
+                     "allow_descendants" : 0,
+                     "label" : "Colombia"
                   },
                   {
-                     "term" : "NCIT_C17181",
-                     "include_root" : 1,
                      "ontology_name" : "NCIT",
+                     "term" : "NCIT_C17181",
+                     "allow_descendants" : 0,
                      "label" : "Switzerland",
-                     "allow_descendants" : 0
+                     "include_root" : 1
                   },
                   {
-                     "allow_descendants" : 0,
+                     "term" : "NCIT_C16903",
                      "ontology_name" : "NCIT",
-                     "label" : "Netherlands",
                      "include_root" : 1,
-                     "term" : "NCIT_C16903"
+                     "label" : "Netherlands",
+                     "allow_descendants" : 0
                   },
                   {
-                     "allow_descendants" : 0,
+                     "ontology_name" : "NCIT",
                      "term" : "NCIT_C16636",
                      "label" : "Germany",
-                     "ontology_name" : "NCIT",
+                     "allow_descendants" : 0,
                      "include_root" : 1
                   },
                   {
-                     "ontology_name" : "NCIT",
                      "include_root" : 1,
                      "label" : "Italy",
+                     "allow_descendants" : 0,
                      "term" : "NCIT_C16761",
-                     "allow_descendants" : 0
+                     "ontology_name" : "NCIT"
                   },
                   {
-                     "allow_descendants" : 0,
+                     "ontology_name" : "NCIT",
                      "term" : "NCIT_C16699",
                      "label" : "Hungary",
-                     "ontology_name" : "NCIT",
+                     "allow_descendants" : 0,
                      "include_root" : 1
                   },
                   {
+                     "term" : "NCIT_C16878",
                      "ontology_name" : "NCIT",
                      "include_root" : 1,
                      "label" : "Morocco",
-                     "term" : "NCIT_C16878",
                      "allow_descendants" : 0
                   },
                   {
+                     "term" : "NCIT_C17152",
                      "ontology_name" : "NCIT",
-                     "label" : "Spain",
                      "include_root" : 1,
-                     "term" : "NCIT_C17152",
-                     "allow_descendants" : 0
+                     "allow_descendants" : 0,
+                     "label" : "Spain"
                   },
                   {
-                     "allow_descendants" : 0,
-                     "ontology_name" : "NCIT",
                      "label" : "Argentina",
+                     "allow_descendants" : 0,
                      "include_root" : 1,
+                     "ontology_name" : "NCIT",
                      "term" : "NCIT_C16305"
                   },
                   {
                      "ontology_name" : "NCIT",
-                     "label" : "Sweden",
-                     "include_root" : 1,
                      "term" : "NCIT_C17180",
-                     "allow_descendants" : 0
+                     "label" : "Sweden",
+                     "allow_descendants" : 0,
+                     "include_root" : 1
                   },
                   {
+                     "include_root" : 1,
+                     "label" : "United Kingdom",
                      "allow_descendants" : 0,
                      "term" : "NCIT_C17233",
-                     "include_root" : 1,
-                     "ontology_name" : "NCIT",
-                     "label" : "United Kingdom"
+                     "ontology_name" : "NCIT"
                   },
                   {
                      "term" : "NCIT_C17002",
                      "ontology_name" : "NCIT",
-                     "label" : "Poland",
                      "include_root" : 1,
-                     "allow_descendants" : 0
+                     "allow_descendants" : 0,
+                     "label" : "Poland"
                   },
                   {
-                     "allow_descendants" : 0,
-                     "include_root" : 1,
                      "ontology_name" : "NCIT",
+                     "term" : "NCIT_C17006",
                      "label" : "Portugal",
-                     "term" : "NCIT_C17006"
+                     "allow_descendants" : 0,
+                     "include_root" : 1
                   },
                   {
-                     "ontology_name" : "NCIT",
-                     "label" : "Austria",
                      "include_root" : 1,
+                     "label" : "Austria",
+                     "allow_descendants" : 0,
                      "term" : "NCIT_C16312",
-                     "allow_descendants" : 0
+                     "ontology_name" : "NCIT"
                   }
                ],
-               "Required" : "mandatory",
-               "Name" : "Gene bank country",
                "Example" : "Italy",
-               "Description" : "Country in which the gene bank resides, particularly important when different countries use the same database infrastructure, e.g. CryoWeb. The controlled vocabulary for countries is from INSDC http://www.insdc.org/documents/country-qualifier-vocabulary",
-               "Allow Multiple" : "no"
+               "Type" : "ontology_id"
             },
             {
+               "Required" : "mandatory",
                "Description" : "the type of the data source the current Inject tool can handle",
-               "Example" : "CryoWeb",
-               "Type" : "limited value",
                "Allow Multiple" : "no",
+               "Type" : "limited value",
+               "Example" : "CryoWeb",
+               "Name" : "Data source type",
                "Valid values" : [
                   "CryoWeb",
                   "CRB-Anim",
                   "Template"
-               ],
-               "Name" : "Data source type",
-               "Required" : "mandatory"
+               ]
             },
             {
+               "Description" : "the version of the data source, suggested to be either the dump date or the version number used by the source",
                "Required" : "mandatory",
                "Name" : "Data source version",
-               "Type" : "text",
-               "Description" : "the version of the data source, suggested to be either the dump date or the version number used by the source",
                "Example" : "2018-04-27, version 1.5",
+               "Type" : "text",
                "Allow Multiple" : "no"
             },
             {
-               "Name" : "Species",
                "Required" : "mandatory",
-               "Description" : "the species of the sample record",
-               "Type" : "ontology_id",
-               "Example" : "Sus scrofa",
-               "Allow Multiple" : "no",
                "Valid terms" : [
                   {
                      "term" : "NCBITaxon_1",
@@ -470,108 +464,136 @@
                      "include_root" : 0,
                      "allow_descendants" : 1
                   }
-               ]
+               ],
+               "Description" : "the species of the sample record",
+               "Allow Multiple" : "no",
+               "Example" : "Sus scrofa",
+               "Type" : "ontology_id",
+               "Name" : "Species"
             }
-         ]
+         ],
+         "name" : "standard"
       },
       {
+         "name" : "organism",
+         "condition" : {
+            "attribute_value_match" : {
+               "Material" : "organism"
+            }
+         },
          "rules" : [
             {
-               "Description" : "Father and/or Mother's animal ID whose record must also be present as part of the subission or BioSamples ID if sample has been previously submitted",
+               "Allow Multiple" : "max 2",
                "Example" : "SAMEA4450075 or 11",
                "Type" : "text",
-               "Allow Multiple" : "max 2",
+               "Name" : "Child of",
                "Required" : "optional",
-               "Name" : "Child of"
+               "Description" : "Father and/or Mother's animal ID whose record must also be present as part of the subission or BioSamples ID if sample has been previously submitted"
             },
             {
-               "Name" : "Supplied breed",
                "Required" : "mandatory",
-               "Type" : "text",
                "Description" : "the breed information contained in the original data source, could be a local language or breed variation",
-               "Example" : "Bunte Bentheimer",
-               "Allow Multiple" : "no"
+               "Name" : "Supplied breed",
+               "Allow Multiple" : "no",
+               "Type" : "text",
+               "Example" : "Bunte Bentheimer"
             },
             {
-               "Name" : "EFABIS Breed country",
                "Required" : "mandatory",
+               "Description" : "the country where the breed locates, the country name should be in the INSDC list",
+               "Name" : "EFABIS Breed country",
                "Allow Multiple" : "no",
                "Type" : "text",
-               "Description" : "the country where the breed locates, the country name should be in the INSDC list",
                "Example" : "Germany"
             },
             {
-               "Description" : "If possible breed will be mapped to a standardised breed ontology, both supplied and mapped breed will be available to end users",
+               "Name" : "Mapped breed",
                "Type" : "ontology_id",
                "Example" : "LBO_0000347",
                "Allow Multiple" : "no",
                "Valid terms" : [
                   {
-                     "allow_descendants" : 1,
+                     "ontology_name" : "LBO",
                      "term" : "LBO_0000000",
-                     "include_root" : 0,
-                     "ontology_name" : "LBO"
+                     "allow_descendants" : 1,
+                     "include_root" : 0
                   }
                ],
-               "Name" : "Mapped breed",
+               "Description" : "If possible breed will be mapped to a standardised breed ontology, both supplied and mapped breed will be available to end users",
                "Required" : "recommended"
             },
             {
-               "Description" : "the sex of the animal",
+               "Example" : "PATO_0000384 (male)",
                "Type" : "ontology_id",
-               "Example" : "PATO:0000384 (male)",
                "Allow Multiple" : "no",
+               "Name" : "Sex",
                "Valid terms" : [
                   {
-                     "allow_descendants" : 0,
                      "include_root" : 1,
-                     "ontology_name" : "PATO",
-                     "term" : "PATO_0000384"
+                     "allow_descendants" : 0,
+                     "term" : "PATO_0000384",
+                     "ontology_name" : "PATO"
                   },
                   {
-                     "allow_descendants" : 0,
-                     "term" : "PATO_0000383",
                      "ontology_name" : "PATO",
+                     "term" : "PATO_0000383",
+                     "allow_descendants" : 0,
                      "include_root" : 1
+                  },
+                  {
+                     "include_root" : 1,
+                     "allow_descendants" : 0,
+                     "term" : "OBI_0000858",
+                     "ontology_name" : "OBI"
                   }
                ],
-               "Name" : "Sex",
+               "Description" : "the sex of the animal",
                "Required" : "mandatory"
             },
             {
-               "Description" : "name of the place where the animal was given birth",
-               "Type" : "text",
-               "Example" : "Germany",
+               "Required" : "recommended",
+               "Description" : "Date when the animal was born",
+               "Name" : "Birth date",
                "Allow Multiple" : "no",
+               "Valid units" : [
+                  "YYYY-MM-DD",
+                  "YYYY-MM",
+                  "YYYY"
+               ],
+               "Type" : "date"
+            },
+            {
+               "Description" : "name of the place where the animal was given birth",
+               "Required" : "recommended",
                "Name" : "Birth location",
-               "Required" : "recommended"
+               "Example" : "Germany",
+               "Type" : "text",
+               "Allow Multiple" : "no"
             },
             {
-               "Allow Multiple" : "no",
+               "Required" : "recommended",
                "Description" : "the longitude of the place where the animal was given birth in decimal degrees",
+               "Allow Multiple" : "no",
                "Type" : "number",
                "Example" : "47.86591",
                "Valid units" : [
                   "decimal degrees"
                ],
-               "Name" : "Birth location longitude",
-               "Required" : "recommended"
+               "Name" : "Birth location longitude"
             },
             {
-               "Allow Multiple" : "no",
+               "Required" : "recommended",
                "Description" : "the latitude of the place where the animal was given birth in decimal degrees",
+               "Name" : "Birth location latitude",
+               "Allow Multiple" : "no",
                "Example" : "12.19115",
-               "Type" : "number",
                "Valid units" : [
                   "decimal degrees"
                ],
-               "Required" : "recommended",
-               "Name" : "Birth location latitude"
+               "Type" : "number"
             },
             {
-               "Required" : "mandatory",
                "Name" : "Birth location accuracy",
-               "Type" : "limited value",
                "Valid values" : [
                   "missing geographic information",
                   "country level",
@@ -579,87 +601,82 @@
                   "precise coordinates",
                   "unknown accuracy level"
                ],
-               "Allow Multiple" : "no"
-            }
-         ],
-         "name" : "organism",
-         "condition" : {
-            "attribute_value_match" : {
-               "Material" : "organism"
+               "Allow Multiple" : "no",
+               "Type" : "limited value",
+               "Required" : "mandatory"
             }
-         }
+         ]
       },
       {
+         "name" : "specimen from organism",
          "condition" : {
             "attribute_value_match" : {
                "Material" : "specimen from organism"
             }
          },
-         "name" : "specimen from organism",
          "rules" : [
             {
-               "Description" : "The data source id or alternative id of the animal from which the sample was collected",
-               "Type" : "text",
                "Example" : "SAMEA4450079",
+               "Type" : "text",
                "Allow Multiple" : "no",
-               "Required" : "mandatory",
-               "Name" : "Derived from"
+               "Name" : "Derived from",
+               "Description" : "The data source id or alternative id of the animal from which the sample was collected",
+               "Required" : "mandatory"
             },
             {
-               "Name" : "Specimen collection protocol",
+               "Description" : "A URL link to the protocol used to collect this sample",
                "Required" : "recommended",
+               "Name" : "Specimen collection protocol",
                "Type" : "uri",
-               "Description" : "A URL link to the protocol used to collect this sample",
                "Example" : "ftp://ftp.faang.ebi.ac.uk/ftp/protocols/samples/WUR_SOP_animal_sampling_20160405.pdf",
                "Allow Multiple" : "no"
             },
             {
-               "Required" : "mandatory",
                "Name" : "Collection date",
+               "Allow Multiple" : "no",
+               "Example" : "17/01/1992",
+               "Type" : "date",
                "Valid units" : [
                   "YYYY-MM-DD",
                   "YYYY-MM",
                   "YYYY"
                ],
-               "Description" : "Date when the sample was collected",
-               "Type" : "date",
-               "Example" : "17/01/1992",
-               "Allow Multiple" : "no"
+               "Required" : "recommended",
+               "Description" : "Date when the sample was collected"
             },
             {
-               "Allow Multiple" : "no",
+               "Required" : "recommended",
                "Description" : "latitude of the place where the sample was collected",
+               "Name" : "Collection place latitude",
+               "Allow Multiple" : "no",
                "Type" : "number",
                "Example" : "47.86591",
                "Valid units" : [
                   "decimal degrees"
-               ],
-               "Required" : "recommended",
-               "Name" : "Collection place latitude"
+               ]
             },
             {
-               "Allow Multiple" : "no",
                "Description" : "longitude of the place where the sample was collected",
-               "Type" : "number",
+               "Required" : "recommended",
                "Example" : "12.19115",
                "Valid units" : [
                   "decimal degrees"
                ],
-               "Required" : "recommended",
+               "Type" : "number",
+               "Allow Multiple" : "no",
                "Name" : "Collection place longitude"
             },
             {
-               "Name" : "Collection place",
-               "Required" : "mandatory",
+               "Required" : "recommended",
                "Description" : "name of the place where the sample was collected",
-               "Type" : "text",
+               "Allow Multiple" : "no",
                "Example" : "Germany",
-               "Allow Multiple" : "no"
+               "Type" : "text",
+               "Name" : "Collection place"
             },
             {
-               "Name" : "Collection place accuracy",
                "Required" : "mandatory",
-               "Allow Multiple" : "no",
+               "Name" : "Collection place accuracy",
                "Valid values" : [
                   "missing geographic information",
                   "country level",
@@ -667,30 +684,27 @@
                   "precise coordinates",
                   "unknown accuracy level"
                ],
+               "Allow Multiple" : "no",
                "Type" : "limited value"
             },
             {
-               "Allow Multiple" : "no",
+               "Description" : "Ontology for organism part where the specimen was taken from",
                "Valid terms" : [
                   {
                      "allow_descendants" : 1,
-                     "ontology_name" : "UBERON",
                      "include_root" : 0,
+                     "ontology_name" : "UBERON",
                      "term" : "UBERON_0001062"
                   }
                ],
+               "Required" : "mandatory",
+               "Name" : "Organism part",
                "Type" : "ontology_id",
-               "Description" : "Ontology for organism part where the specimen was taken from",
                "Example" : "UBERON_0001968 (semen)",
-               "Name" : "Organism part",
-               "Required" : "mandatory"
+               "Allow Multiple" : "no"
             },
             {
-               "Required" : "recommended",
-               "Name" : "Developmental stage",
-               "Type" : "ontology_id",
                "Description" : "developmental stage of the animal from which the specimen is collected",
-               "Example" : "EFO_0001272 (adult)",
                "Valid terms" : [
                   {
                      "term" : "EFO_0000399",
@@ -699,31 +713,32 @@
                      "allow_descendants" : 1
                   }
                ],
+               "Required" : "recommended",
+               "Name" : "Developmental stage",
+               "Type" : "ontology_id",
+               "Example" : "EFO_0001272 (adult)",
                "Allow Multiple" : "no"
             },
             {
+               "Name" : "Physiological stage",
                "Type" : "ontology_id",
-               "Description" : "physiological stage of the animal from which the specimen is collected",
                "Example" : "PATO_0001701 (mature)",
+               "Allow Multiple" : "no",
                "Valid terms" : [
                   {
-                     "term" : "PATO_0000261",
-                     "ontology_name" : "PATO",
                      "include_root" : 0,
-                     "allow_descendants" : 1
+                     "allow_descendants" : 1,
+                     "term" : "PATO_0000261",
+                     "ontology_name" : "PATO"
                   }
                ],
-               "Allow Multiple" : "no",
-               "Required" : "recommended",
-               "Name" : "Physiological stage"
+               "Description" : "physiological stage of the animal from which the specimen is collected",
+               "Required" : "recommended"
             },
             {
+               "Allow Multiple" : "no",
                "Example" : "3 years",
-               "Description" : "Animal age at the point of collection.",
                "Type" : "number",
-               "Allow Multiple" : "no",
-               "Name" : "Animal age at collection",
-               "Required" : "optional",
                "Valid units" : [
                   "minutes",
                   "hours",
@@ -737,22 +752,27 @@
                   "week",
                   "month",
                   "year"
-               ]
+               ],
+               "Name" : "Animal age at collection",
+               "Required" : "optional",
+               "Description" : "Animal age at the point of collection."
             },
             {
-               "Allow Multiple" : "no",
+               "Required" : "recommended",
+               "Description" : "Either a link to a web page giving information on who to contact or providing access of the data or an e-mail address to contact about availability. E-mail addresses should be prefixed with 'mailto:', e.g. 'mailto:samples@example.ac.uk'. In either case, long term support of the web page or e-mail address is necessary. Group e-mail addresses are preferable to indiviudal accounts. If neither available, please use the value no longer available",
                "Valid values" : [
                   "no longer available",
-                  "mailto:a.valid@email"
+                  "mailto:a.valid@email",
+                  "https://a.valid.weblink"
                ],
-               "Type" : "limited value",
-               "Description" : "Either an e-mail address to contact about availability or a link to a web page giving information on who to contact. E-mail addresses should be prefixed with 'mailto:', e.g. 'mailto:samples@example.ac.uk'. In either case, long term support of the web page or e-mail address is necessary. Group e-mail addresses are preferable to indiviudal accounts.",
-               "Example" : "mailto:vdc-internal@ebi.ac.uk",
                "Name" : "Availability",
-               "Required" : "recommended"
+               "Allow Multiple" : "no",
+               "Type" : "limited value",
+               "Example" : "mailto:vdc-internal@ebi.ac.uk, https://crb-anim.fr/access-to-collection/"
             },
             {
-               "Required" : "recommended",
+               "Allow Multiple" : "no",
+               "Type" : "limited value",
                "Name" : "Sample storage",
                "Valid values" : [
                   "ambient temperature",
@@ -767,14 +787,12 @@
                   "paraffin block at ambient temperatures (+15 to +30 degrees Celsius)",
                   "freeze dried"
                ],
-               "Allow Multiple" : "no",
-               "Type" : "limited value",
+               "Required" : "recommended",
                "Description" : "How the sample was stored. Temperatures are in degree Celsius. 'Frozen, vapor phase' refers to storing samples above liquid nitrogen in the vapor"
             },
             {
+               "Required" : "recommended",
                "Description" : "How the sample was prepared for storage",
-               "Type" : "limited value",
-               "Allow Multiple" : "no",
                "Valid values" : [
                   "cryopreservation in liquid nitrogen (dead tissue)",
                   "cryopreservation in dry ice (dead tissue)",
@@ -787,14 +805,15 @@
                   "freeze dried"
                ],
                "Name" : "Sample storage processing",
-               "Required" : "recommended"
+               "Allow Multiple" : "no",
+               "Type" : "limited value"
             },
             {
-               "Description" : "How long between the sample being taken and the assay experiment preperations commencing. If sample preperations were then left in intermediate stages after preperation commenced, for example as sheared chromatin, then this should be made clear in your protocols",
-               "Type" : "number",
-               "Allow Multiple" : "no",
                "Required" : "optional",
+               "Description" : "How long between the sample being taken and the assay experiment preperations commencing. If sample preperations were then left in intermediate stages after preperation commenced, for example as sheared chromatin, then this should be made clear in your protocols",
                "Name" : "Sampling to preparation interval",
+               "Allow Multiple" : "no",
+               "Type" : "number",
                "Valid units" : [
                   "minutes",
                   "hours",
@@ -813,7 +832,7 @@
          ]
       }
    ],
-   "further_details_iri" : "https://github.com/bioinformatics-ptp/IMAGE-metadata/blob/master/README.md",
-   "description" : "Validation rules for the IMAGE project."
+   "name" : "IMAGE sample metadata rules",
+   "further_details_iri" : "https://github.com/bioinformatics-ptp/IMAGE-metadata/blob/master/README.md"
 }
 
diff --git a/image_validation/static_parameters.py b/image_validation/static_parameters.py
index 0c9872a..40c5ada 100644
--- a/image_validation/static_parameters.py
+++ b/image_validation/static_parameters.py
@@ -6,6 +6,6 @@ from . import use_ontology
 
 ruleset_filename = os.path.join(
     os.path.dirname(use_ontology.__file__),
-    "sample_ruleset_v1.4.json")
+    "sample_ruleset_v1.5.json")
 
 ontology_library = use_ontology.OntologyCache()
diff --git a/scripts/main.py b/scripts/main.py
index 28f8df2..fb3c660 100755
--- a/scripts/main.py
+++ b/scripts/main.py
@@ -9,22 +9,31 @@ from typing import List
 from image_validation import validation, ValidationResult, static_parameters
 
 logger = logging.getLogger(__name__)
-logging.basicConfig(format='%(asctime)s\t%(levelname)s:\t%(name)s line %(lineno)s\t%(message)s', level=logging.INFO)
+logging.basicConfig(
+    format='%(asctime)s\t%(levelname)s:\t%(name)s line %(lineno)s\t%(message)s',
+    level=logging.INFO)
 
 # read JSON into memory
 logger.info("START")
 
-basedir = os.path.dirname(os.path.abspath(__file__))
-filename = os.path.join(basedir, 'submission_example.json')
+# a-la django style
+basedir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+filename = os.path.join(basedir, 'test_data/submission_example.json')
 
 try:
     with open(filename) as infile:
         data = json.load(infile)
+
 except FileNotFoundError:
     logger.critical("Could not find the file " + filename)
     exit(1)
+
 except json.decoder.JSONDecodeError as e:
-    logger.critical("The provided file " + filename + " is not a valid JSON file. Reason: " + str(e))
+    logger.critical(
+        "The provided file " +
+        filename +
+        " is not a valid JSON file. Reason: " +
+        str(e))
     exit(1)
 
 data = data['sample']
@@ -39,16 +48,19 @@ ruleset = validation.read_in_ruleset(static_parameters.ruleset_filename)
 ruleset_check = validation.check_ruleset(ruleset)
 if ruleset_check:
     validation.deal_with_errors(ruleset_check)
-    exit()
+    logger.error("Found errors in check_ruleset!")
+
 logger.info("Loaded the ruleset")
 submission_result: List[ValidationResult.ValidationResultRecord] = []
 for record in data:
-    logger.info("Validate record "+record['alias'])
+    logger.info("Validate record " + record['alias'])
     record_result = ruleset.validate(record)
-    record_result = validation.context_validation(record['attributes'], record_result)
+    record_result = validation.context_validation(
+        record['attributes'], record_result)
     if record_result.is_empty():
         record_result.add_validation_result_column(
-            ValidationResult.ValidationResultColumn("Pass", "", record_result.record_id))
+            ValidationResult.ValidationResultColumn(
+                    "Pass", "", record_result.record_id))
     submission_result.append(record_result)
 # pprint.pprint(rules)
 summary = validation.deal_with_validation_results(submission_result)
diff --git a/setup.py b/setup.py
index 083d74b..7e08697 100644
--- a/setup.py
+++ b/setup.py
@@ -35,7 +35,7 @@ with open('README.rst') as readme_file:
 with open('HISTORY.rst') as history_file:
     history = history_file.read()
 
-requirements = ['python-dateutil==2.7.5', 'requests']
+requirements = ['python-dateutil', 'requests']
 
 setup_requirements = ['pytest-runner', ]
 


Test patch:
diff --git a/test_data/image_animal.json b/test_data/image_animal.json
new file mode 100644
index 0000000..c69417d
--- /dev/null
+++ b/test_data/image_animal.json
@@ -0,0 +1,139 @@
+{
+  "alias": "animal_35",
+  "attributes": {
+    "Alternative id": [
+      {
+        "value": "855"
+      }
+    ],
+    "Birth location accuracy": [
+      {
+        "value": "missing geographic information"
+      }
+    ],
+    "Data source ID": [
+      {
+        "value": "ANIMAL:::ID:::CS05_1999"
+      }
+    ],
+    "Data source type": [
+      {
+        "value": "CryoWeb"
+      }
+    ],
+    "Data source version": [
+      {
+        "value": "version 0.1"
+      }
+    ],
+    "EFABIS Breed country": [
+      {
+        "value": "Italy"
+      }
+    ],
+    "Gene bank country": [
+      {
+        "terms": [
+          {
+            "url": "http://purl.obolibrary.org/obo/NCIT_C16761"
+          }
+        ],
+        "value": "Italy"
+      }
+    ],
+    "Gene bank name": [
+      {
+        "value": "CryoWeb Subset"
+      }
+    ],
+    "Material": [
+      {
+        "terms": [
+          {
+            "url": "http://purl.obolibrary.org/obo/OBI_0100026"
+          }
+        ],
+        "value": "organism"
+      }
+    ],
+    "Organization name": [
+      {
+        "value": "IBBA-CNR"
+      }
+    ],
+    "Organization role": [
+      {
+        "terms": [
+          {
+            "url": "http://www.ebi.ac.uk/efo/EFO_0001741"
+          }
+        ],
+        "value": "submitter"
+      }
+    ],
+    "Person affiliation": [
+      {
+        "value": "IBBA-CNR"
+      }
+    ],
+    "Person email": [
+      {
+        "value": "mailto:cozzi@ibba.cnr.it"
+      }
+    ],
+    "Person last name": [
+      {
+        "value": "Cozzi"
+      }
+    ],
+    "Person role": [
+      {
+        "terms": [
+          {
+            "url": "http://www.ebi.ac.uk/efo/EFO_0001741"
+          }
+        ],
+        "value": "submitter"
+      }
+    ],
+    "Project": [
+      {
+        "value": "IMAGE"
+      }
+    ],
+    "Sex": [
+      {
+        "terms": [
+          {
+            "url": "http://purl.obolibrary.org/obo/PATO_0000384"
+          }
+        ],
+        "value": "male"
+      }
+    ],
+    "Species": [
+      {
+        "terms": [
+          {
+            "url": "http://purl.obolibrary.org/obo/NCBITaxon_9823"
+          }
+        ],
+        "value": "Sus scrofa"
+      }
+    ],
+    "Submission title": [
+      {
+        "value": "Italian Cryoweb Pigs"
+      }
+    ],
+    "Supplied breed": [
+      {
+        "value": "Cinta Senese"
+      }
+    ]
+  },
+  "releaseDate": "2018-11-09",
+  "sampleRelationships": [],
+  "taxonId": 9823,
+  "title": "ANIMAL:::ID:::CS05_1999"
+}
\ No newline at end of file
diff --git a/test_data/image_sample.json b/test_data/image_sample.json
new file mode 100644
index 0000000..e99022e
--- /dev/null
+++ b/test_data/image_sample.json
@@ -0,0 +1,155 @@
+{
+  "alias": "sample_257",
+  "attributes": {
+    "Alternative id": [
+      {
+        "value": "1835"
+      }
+    ],
+    "Availability": [
+      {
+        "value": "no longer available"
+      }
+    ],
+    "Collection date": [
+      {
+        "units": "YYYY-MM-DD",
+        "value": "1999-06-23"
+      }
+    ],
+    "Collection place": [
+      {
+        "value": "Italy"
+      }
+    ],
+    "Collection place accuracy": [
+      {
+        "value": "missing geographic information"
+      }
+    ],
+    "Data source ID": [
+      {
+        "value": "CS05_1999_IBBACNR_PTP_23.06.1999"
+      }
+    ],
+    "Data source type": [
+      {
+        "value": "CryoWeb"
+      }
+    ],
+    "Data source version": [
+      {
+        "value": "version 0.1"
+      }
+    ],
+    "Derived from": [
+      {
+        "value": "ANIMAL:::ID:::CS05_1999"
+      }
+    ],
+    "Gene bank country": [
+      {
+        "terms": [
+          {
+            "url": "http://purl.obolibrary.org/obo/NCIT_C16761"
+          }
+        ],
+        "value": "Italy"
+      }
+    ],
+    "Gene bank name": [
+      {
+        "value": "CryoWeb Subset"
+      }
+    ],
+    "Material": [
+      {
+        "terms": [
+          {
+            "url": "http://purl.obolibrary.org/obo/OBI_0001479"
+          }
+        ],
+        "value": "specimen from organism"
+      }
+    ],
+    "Organism part": [
+      {
+        "terms": [
+          {
+            "url": "http://purl.obolibrary.org/obo/UBERON_0001968"
+          }
+        ],
+        "value": "semen"
+      }
+    ],
+    "Organization name": [
+      {
+        "value": "IBBA-CNR"
+      }
+    ],
+    "Organization role": [
+      {
+        "terms": [
+          {
+            "url": "http://www.ebi.ac.uk/efo/EFO_0001741"
+          }
+        ],
+        "value": "submitter"
+      }
+    ],
+    "Person affiliation": [
+      {
+        "value": "IBBA-CNR"
+      }
+    ],
+    "Person email": [
+      {
+        "value": "mailto:cozzi@ibba.cnr.it"
+      }
+    ],
+    "Person last name": [
+      {
+        "value": "Cozzi"
+      }
+    ],
+    "Person role": [
+      {
+        "terms": [
+          {
+            "url": "http://www.ebi.ac.uk/efo/EFO_0001741"
+          }
+        ],
+        "value": "submitter"
+      }
+    ],
+    "Project": [
+      {
+        "value": "IMAGE"
+      }
+    ],
+    "Species": [
+      {
+        "terms": [
+          {
+            "url": "http://purl.obolibrary.org/obo/NCBITaxon_9823"
+          }
+        ],
+        "value": "Sus scrofa"
+      }
+    ],
+    "Submission title": [
+      {
+        "value": "Italian Cryoweb Pigs"
+      }
+    ]
+  },
+  "releaseDate": "2018-10-09",
+  "sampleRelationships": [
+    {
+      "alias": "animal_35",
+      "relationshipNature": "derived from"
+    }
+  ],
+  "taxonId": 9823,
+  "title": "CS05_1999_IBBACNR_PTP_23.06.1999"
+}
\ No newline at end of file
diff --git a/scripts/submission_example.json b/test_data/submission_example.json
similarity index 100%
rename from scripts/submission_example.json
rename to test_data/submission_example.json
diff --git a/tests/test_ruleset.py b/tests/test_ruleset.py
index 7bc2b8e..2f80299 100644
--- a/tests/test_ruleset.py
+++ b/tests/test_ruleset.py
@@ -8,6 +8,9 @@ from image_validation import ValidationResult
 
 
 class TestRuleset(unittest.TestCase):
+    def setUp(self):
+        # to see all differences in assertEqual
+        self.maxDiff = None
 
     def test_ontology_condition_types(self):
         self.assertRaises(TypeError, Ruleset.OntologyCondition, True, False, True, True)
@@ -17,13 +20,10 @@ class TestRuleset(unittest.TestCase):
 
     def test_ontology_condition(self):
         filename = "test_data/ruleset_allowed_terms.json"
-        try:
-            with open(filename) as infile:
-                data = json.load(infile)
-        except FileNotFoundError:
-            exit(1)
-        except json.decoder.JSONDecodeError as e:
-            exit(1)
+
+        # NOTE: file are supposed to exists while testing
+        with open(filename) as infile:
+            data = json.load(infile)
 
         # using RuleField to test accessing attributes
         test_rule_field = Ruleset.RuleField("test", "ontology_id", "recommended")
@@ -100,13 +100,10 @@ class TestRuleset(unittest.TestCase):
 
         # test check_ontology_allowed while testing more complicated set_allowed_terms
         filename = "test_data/ruleset_allowed_terms.json"
-        try:
-            with open(filename) as infile:
-                data = json.load(infile)
-        except FileNotFoundError:
-            exit(1)
-        except json.decoder.JSONDecodeError as e:
-            exit(1)
+
+        # NOTE: file are supposed to exists while testing
+        with open(filename) as infile:
+            data = json.load(infile)
 
         rule_field_1.set_allowed_terms(data)
         # exact match, include self
@@ -135,6 +132,18 @@ class TestRuleset(unittest.TestCase):
         rule_section = Ruleset.RuleSection('test')
         self.assertRaises(TypeError, rule_section.add_rule, "rule")
 
+    def test_rule_section_serializer(self):
+        reference = {
+            'conditions': {},
+            'name': 'test',
+            'rule_names': {},
+            'rules': {}}
+
+        rule_section = Ruleset.RuleSection('test')
+        test = json.loads(rule_section.toJSON())
+
+        self.assertEqual(reference, test)
+
     def test_rule_section(self):
         ruleset = validation.read_in_ruleset("test_data/test_ruleset.json")
         warships_section = ruleset.get_section_by_name('warships')
@@ -161,15 +170,14 @@ class TestRuleset(unittest.TestCase):
 
         # test meet condition
         filename = "test_data/test_data.json"
-        try:
-            with open(filename) as infile:
-                data = json.load(infile)
-        except FileNotFoundError:
-            exit(1)
-        except json.decoder.JSONDecodeError as e:
-            exit(1)
+
+        # NOTE: file are supposed to exists while testing
+        with open(filename) as infile:
+            data = json.load(infile)
+
         transport_data = data[0]
         warship_data = data[1]
+
         self.assertRaises(TypeError, warships_section.meet_condition, 'dict')
         self.assertRaises(TypeError, warships_section.meet_condition, 12)
         self.assertRaises(KeyError, warships_section.meet_condition, transport_data['attributes'])
@@ -202,15 +210,18 @@ class TestRuleset(unittest.TestCase):
         self.assertListEqual(ruleset.get_all_section_names(), section_names)
 
         filename = "test_data/test_data.json"
-        try:
-            with open(filename) as infile:
-                data = json.load(infile)
-        except FileNotFoundError:
-            exit(1)
-        except json.decoder.JSONDecodeError as e:
-            exit(1)
+
+        # NOTE: file are supposed to exists while testing
+        with open(filename) as infile:
+            data = json.load(infile)
+
         ruleset.validate(data[0], "id")
 
+    def test_rule_set_serializer(self):
+        ruleset = validation.read_in_ruleset("test_data/test_ruleset.json")
+        # test string serialization
+        self.assertIsInstance(ruleset.toJSON(), str)
+
     def test_validate(self):
         ruleset = validation.read_in_ruleset("test_data/test_ruleset.json")
         expected_result: Dict[str, List[str]] = {
@@ -293,13 +304,11 @@ class TestRuleset(unittest.TestCase):
         self.maxDiff = None
         for error_type in expected_result.keys():
             filename = "test_data/data/test_error_rule_" + error_type + ".json"
-            try:
-                with open(filename) as infile:
-                    data = json.load(infile)
-            except FileNotFoundError:
-                exit(1)
-            except json.decoder.JSONDecodeError as e:
-                exit(1)
+
+            # NOTE: file are supposed to exists while testing
+            with open(filename) as infile:
+                data = json.load(infile)
+
             submission_result: List[ValidationResult.ValidationResultRecord] = []
             actual_values: List[str] = []
             for record in data:
@@ -317,4 +326,4 @@ class TestRuleset(unittest.TestCase):
 
 
 if __name__ == '__main__':
-    unittest.main()
\ No newline at end of file
+    unittest.main()
diff --git a/tests/test_validation.py b/tests/test_validation.py
index 525e7fa..2f36a85 100644
--- a/tests/test_validation.py
+++ b/tests/test_validation.py
@@ -9,6 +9,9 @@ from image_validation import ValidationResult
 
 
 class TestValidation(unittest.TestCase):
+    def setUp(self):
+        # to see all differences in assertEqual
+        self.maxDiff = None
 
     def test_read_in_ruleset_type(self):
         self.assertRaises(TypeError, validation.read_in_ruleset, 12)
@@ -48,6 +51,7 @@ class TestValidation(unittest.TestCase):
         results = validation.check_ruleset(ruleset)
         self.assertListEqual(expected, results)
         expected = []
+
         ruleset = validation.read_in_ruleset(static_parameters.ruleset_filename)
         results = validation.check_ruleset(ruleset)
         self.assertListEqual(expected, results)
@@ -196,6 +200,38 @@ class TestValidation(unittest.TestCase):
             existing_results = validation.context_validation(record['attributes'], existing_results)
             self.assertListEqual(existing_results.get_messages(), expected[i])
 
+    def test_image_animal(self):
+        # get image test file
+        filename = "test_data/image_animal.json"
+
+        # NOTE: file are supposed to exists while testing
+        with open(filename) as infile:
+            animal = json.load(infile)
+
+        # read ruleset
+        ruleset = validation.read_in_ruleset(
+                static_parameters.ruleset_filename)
+
+        # now check animal
+        result = ruleset.validate(animal).get_messages()
+        self.assertEqual(result, [])
+
+    def test_image_sample(self):
+        # get image test file
+        filename = "test_data/image_sample.json"
+
+        # NOTE: file are supposed to exists while testing
+        with open(filename) as infile:
+            sample = json.load(infile)
+
+        # read ruleset
+        ruleset = validation.read_in_ruleset(
+                static_parameters.ruleset_filename)
+
+        # now check sample
+        result = ruleset.validate(sample).get_messages()
+        self.assertEqual(result, [])
+
 
 if __name__ == '__main__':
     unittest.main()