Repository: phac-nml/metadatatransformation
Pull request #10: Adds the Populate Transformation
Author: login: emarinier
Created: 2025-03-12T17:46:53Z | Merged: 2025-03-12T21:24:09Z

# Populate Transformation

Adds the population transformation. When given a column name and a value, populates that column for every sample with the provided value.

## Example

### Parameters

`--populate_column my_header`
`--populate_value my_value`

### Sample Sheet

```
sample,sample_name,metadata_1
sample1,"ABC",1.1
sample2,"DEF",2.1
sample3,"GHI",3.1
```

### results.csv

```
sample,sample_name,metadata_1,my_header
sample1,ABC,1.1,my_value
sample2,DEF,2.1,my_value
sample3,GHI,3.1,my_value
```

### transformation.csv

```
sample,my_header
sample1,my_value
sample2,my_value
sample3,my_value
```

### iridanext.output.json

```
{
    "files": {
        "global": [
            {
                "path": "transformation/results.csv"
            }
        ],
        "samples": {
            
        }
    },
    "metadata": {
        "samples": {
            "sample1": {
                "my_header": "my_value"
            },
            "sample2": {
                "my_header": "my_value"
            },
            "sample3": {
                "my_header": "my_value"
            }
        }
    }
}
```


Diff:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 40e46e1..3cdb29f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### `Added`
 
-- `earliest` transformation: finds earliest date among metadata
+- `earliest` transformation: finds earliest date among metadata [PR #9](https://github.com/phac-nml/metadatatransformation/pull/9)
+- `populate` transformation: populates an output column with a specific value [PR #10](https://github.com/phac-nml/metadatatransformation/pull/10)
 
 ## [1.0.0] - 2025/03/07
 
diff --git a/README.md b/README.md
index 0339b18..f49f049 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,7 @@ You may specify the metadata transformation with the `--transformation` paramete
 | lock           | Locks, or copies and locks, the metadata in IRIDA Next.                                                                                                    |
 | age            | Calculates the age between the first and second metadata columns. Ages under 2 years old are calculated as (days/365) years old, showing 4 decimal places. |
 | earliest       | Reports the earliest date among the metadata columns.                                                                                                      |
+| populate       | Populates an output column with a specific value.                                                                                                          |
 
 ## Lock Parameters
 
@@ -78,9 +79,15 @@ The following parameters can be used to rename CSV-generated output columns as f
 - `--metadata_6_header`: names the first metadata_6 column header
 - `--metadata_7_header`: names the first metadata_7 column header
 - `--metadata_8_header`: names the first metadata_8 column header
+- `--earliest_header`: names the earliest date column header and related output columns
 
 The above parameters will only affect the `results.csv` file and not the information returned to IRIDA Next. The earliest date column will be reported as `earliest_date` in `results.csv`, `transformation.csv`, and the `iridanext.output.json` file, which is returned to IRIDA Next.
 
+## Populate Parameters
+
+- `--populate_header`: names the header of the column to populate with `populate_value`
+- `--populate_value`: the value to populate every entry within the `populate_header` column
+
 ## Other Parameters
 
 Other parameters (defaults from nf-core) are defined in [nextflow_schema.json](nextflow_schema.json).
diff --git a/bin/transform.py b/bin/transform.py
index b028160..85f503d 100755
--- a/bin/transform.py
+++ b/bin/transform.py
@@ -14,6 +14,8 @@
 SAMPLE_NAME_HEADER = "sample_name"
 AGE_HEADER = "age"
 EARLIEST_HEADER = "earliest_date"
+POPULATE_HEADER = "populated"
+
 VALID_HEADER_EXTENSION = "_valid"
 ERROR_HEADER_EXTENSION = "_error"
 
@@ -25,6 +27,7 @@
 LOCK = "lock"
 AGE = "age"
 EARLIEST = "earliest"
+POPULATE = "populate"
 
 # Output Files:
 RESULTS_PATH = "results.csv"
@@ -35,10 +38,19 @@
 AGE_THRESHOLD = 2 # Ages less than this will include a decimal component.
 DAYS_IN_YEAR = 365.0
 COLUMN_WISE = 0 # i.e. axis=0 // axis="columns"
+POPULATE_VALUE = "NA"
 
 def remove_empty_columns(metadata):
     metadata.dropna(axis="columns", how="all", inplace=True)
 
+def populate(metadata, populate_header, populate_value):
+    metadata_readable = metadata.copy(deep=True)
+    metadata_readable[[populate_header]] = populate_value
+
+    metadata_irida = metadata_readable[[SAMPLE_HEADER, populate_header]].copy(deep=True)
+
+    return metadata_readable, metadata_irida
+
 def find_earliest_date(row):
     earliest = ""
     earliest_valid = False
@@ -178,12 +190,16 @@ def main():
 
     parser.add_argument("input", type=pathlib.Path,
                         help="The CSV-formatted input file to transform.")
-    parser.add_argument("transformation", choices=[LOCK, AGE, EARLIEST],
+    parser.add_argument("transformation", choices=[LOCK, AGE, EARLIEST, POPULATE],
                         help="The type of transformation to perform.")
     parser.add_argument("--age_header", default=AGE_HEADER, required=False,
                         help="The output column header for the calculated age.")
     parser.add_argument("--earliest_header", default=EARLIEST_HEADER, required=False,
                         help="The output column header for the earliest date.")
+    parser.add_argument("--populate_header", default=POPULATE_HEADER, required=False,
+                        help="The output column header for the populate transformation.")
+    parser.add_argument("--populate_value", default=POPULATE_VALUE, required=False,
+                        help="The value to populate the specified column with for the populate transformation.")
 
     args = parser.parse_args()
     metadata = pandas.read_csv(args.input)
@@ -209,5 +225,12 @@ def main():
         metadata_readable.to_csv(RESULTS_PATH, index=False)
         metadata_irida.to_csv(TRANSFORMATION_PATH, index=False)
 
+    elif (args.transformation == POPULATE):
+        metadata_readable, metadata_irida = populate(metadata, args.populate_header, args.populate_value)
+
+        remove_empty_columns(metadata_irida)
+        metadata_readable.to_csv(RESULTS_PATH, index=False)
+        metadata_irida.to_csv(TRANSFORMATION_PATH, index=False)
+
 if __name__ == '__main__':
     main()
diff --git a/conf/modules.config b/conf/modules.config
index 0cdb177..8fe2fe3 100644
--- a/conf/modules.config
+++ b/conf/modules.config
@@ -35,6 +35,7 @@ process {
             mode: params.publish_dir_mode,
             saveAs: { filename -> filename.equals('versions.yml') ? null : filename }
         ]
-        ext.args = "--age_header \"${params.age_header}\" --earliest_header \"${params.earliest_header}\""
+        ext.args = ("--age_header \"${params.age_header}\" --earliest_header \"${params.earliest_header}\" " +
+                    "--populate_header \"${params.populate_header}\" --populate_value \"${params.populate_value}\"")
     }
 }
diff --git a/docs/output.md b/docs/output.md
index b746cbe..c932960 100644
--- a/docs/output.md
+++ b/docs/output.md
@@ -17,6 +17,7 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d
 - [Lock](#lock) - Locks the metadata for IRIDA Next.
 - [Age](#age) - Calculates the age between the first and second metadata columns.
 - [Earliest](#earliest) - Determines the earliest date among metadata columns.
+- [Populate](#populate) - Populates a specified column with a specified value. Existing values will be overwritten if the column already exists.
 
 ### Lock
 
@@ -49,4 +50,15 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d
 
 </details>
 
+### Populate
+
+<details markdown="1">
+<summary>Output files</summary>
+
+- `transformation/`
+  - A CSV-formatted file for reporting to IRIDA Next the populated column for each sample: `transformation.csv`
+  - A user-intended CSV-formatted file for reference: `result.csv`
+
+</details>
+
 [Nextflow](https://www.nextflow.io/docs/latest/tracing.html) provides excellent functionality for generating various reports relevant to the running and execution of the pipeline. This will allow you to troubleshoot errors with the running of the pipeline, and also provide you with other information such as launch commands, run times and resource usage.
diff --git a/docs/usage.md b/docs/usage.md
index 22bd9ed..59de859 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -102,6 +102,49 @@ You may wish to specify the `--metadata_1_header` through `--metadata_8_header`
 
 If at least one metadata column contains non-empty data that does not conform to the expected "YYYY-MM-DD" date format, then the sample will report an error and no date will be reported for the sample (even if one other valid date appears among the invalid metadata for that sample).
 
+### Populate
+
+The populate transformation may be run as follows:
+
+```bash
+nextflow run phac-nml/metadatatransformation -profile singularity --input tests/data/samplesheets/populate/basic.csv --outdir results --transformation populate --populate_header "new_header" --populate_value "new_value"
+```
+
+For this transformation, all input metadata (`metadata_1` through `metadata_8`) will be ignored. However, the transformation will write or overwrite the input metadata as specified by `--populate_header`. The value specified by `--populate_value` will be written for every sample under the column specified by `--populate_header`.
+
+For example, when running the transformation with the following sample sheet:
+
+```
+sample,sample_name,metadata_1,metadata_2,metadata_3,metadata_4,metadata_5,metadata_6,metadata_7,metadata_8
+sample1,"ABC",1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8
+sample2,"DEF",2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8
+sample3,"GHI",3.1,3.2,3.3,3.4,3.5,3.6,3.7,3.8
+```
+
+and command:
+
+```bash
+nextflow run phac-nml/metadatatransformation -profile singularity --input tests/data/samplesheets/populate/basic.csv --outdir results --transformation populate --populate_header "new_header" --populate_value "new_value"
+```
+
+following output `results.csv` file will be generated:
+
+```
+sample,sample_name,metadata_1,metadata_2,metadata_3,metadata_4,metadata_5,metadata_6,metadata_7,metadata_8,new_header
+sample1,ABC,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,new_value
+sample2,DEF,2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8,new_value
+sample3,GHI,3.1,3.2,3.3,3.4,3.5,3.6,3.7,3.8,new_value
+```
+
+and the following `transformation.csv` file (written back to IRIDA Next) will be generated:
+
+```
+sample,new_header
+sample1,new_value
+sample2,new_value
+sample3,new_value
+```
+
 ### Reproducibility
 
 It is a good idea to specify a pipeline version when running the pipeline on your data. This ensures that a specific version of the pipeline code and software are used when you run your pipeline. If you keep using the same tag, you'll be running the same version of the pipeline, even if there have been changes to the code since.
diff --git a/nextflow.config b/nextflow.config
index ca54b04..4233e4e 100644
--- a/nextflow.config
+++ b/nextflow.config
@@ -59,6 +59,10 @@ params {
 
     // Earliest date transformation
     earliest_header = "earliest_date"
+
+    // Populate value transformation
+    populate_header = "populated_header"
+    populate_value  = "populated_value"
 }
 
 // Load base.config by default for all pipelines
diff --git a/nextflow_schema.json b/nextflow_schema.json
index 7b0211c..916c524 100644
--- a/nextflow_schema.json
+++ b/nextflow_schema.json
@@ -48,7 +48,7 @@
                     "type": "string",
                     "description": "The type of transformation to perform.",
                     "default": "lock",
-                    "enum": ["lock", "age", "earliest"]
+                    "enum": ["lock", "age", "earliest", "populate"]
                 },
                 "age_header": {
                     "type": "string",
@@ -61,6 +61,18 @@
                     "default": "earliest_date",
                     "description": "The column header name for the earliest date from among metadata columns for the earliest date metadata transformation.",
                     "pattern": "^[^\\t\\\",]*$"
+                },
+                "populate_header": {
+                    "type": "string",
+                    "default": "populated_header",
+                    "description": "The column header name for the populate value transformation.",
+                    "pattern": "^[^\\t\\\",]*$"
+                },
+                "populate_value": {
+                    "type": "string",
+                    "default": "populated_value",
+                    "description": "The value specified will be written for every sample under the specified column for the population transformation.",
+                    "pattern": "^[^\\t\\\",]*$"
                 }
             }
         },
diff --git a/tests/data/samplesheets/populate/basic.csv b/tests/data/samplesheets/populate/basic.csv
new file mode 100644
index 0000000..c34a2fc
--- /dev/null
+++ b/tests/data/samplesheets/populate/basic.csv
@@ -0,0 +1,4 @@
+sample,sample_name,metadata_1,metadata_2,metadata_3,metadata_4,metadata_5,metadata_6,metadata_7,metadata_8
+sample1,"ABC",1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8
+sample2,"DEF",2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8
+sample3,"GHI",3.1,3.2,3.3,3.4,3.5,3.6,3.7,3.8
diff --git a/tests/data/samplesheets/populate/missing_metadata.csv b/tests/data/samplesheets/populate/missing_metadata.csv
new file mode 100644
index 0000000..f9672c2
--- /dev/null
+++ b/tests/data/samplesheets/populate/missing_metadata.csv
@@ -0,0 +1,4 @@
+sample,sample_name,metadata_1,metadata_2,metadata_3,metadata_4,metadata_5,metadata_6,metadata_7,metadata_8
+sample1,"ABC",,1.2,1.3,,1.5,1.6,,
+sample2,,2.1,,2.3,2.4,,2.6,2.7,
+sample3,"GHI",3.1,3.2,,3.4,3.5,,3.7,
diff --git a/tests/pipelines/populate.nf.test b/tests/pipelines/populate.nf.test
new file mode 100644
index 0000000..d7499cf
--- /dev/null
+++ b/tests/pipelines/populate.nf.test
@@ -0,0 +1,308 @@
+nextflow_pipeline {
+
+    name "Metadata Transformation: Populate"
+    script "main.nf"
+
+    test("Basic") {
+        tag "pipeline_populate"
+        tag "pipeline_populate_basic"
+
+        when {
+            params {
+                input = "$baseDir/tests/data/samplesheets/populate/basic.csv"
+                outdir = "results"
+
+                transformation = "populate"
+                populate_header = "my_header"
+                populate_value = "my_value"
+            }
+        }
+
+        then {
+            assert workflow.success
+            assert path("$launchDir/results").exists()
+
+            // Check Transformation (Machine-Readable)
+            def transformation = path("$launchDir/results/transformation/transformation.csv")
+            assert transformation.exists()
+
+            assert transformation.text.contains("sample,my_header")
+            assert transformation.text.contains("sample1,my_value")
+            assert transformation.text.contains("sample2,my_value")
+            assert transformation.text.contains("sample3,my_value")
+
+            // Check Results (Human-Readable)
+            def results = path("$launchDir/results/transformation/results.csv")
+            assert results.exists()
+
+            assert results.text.contains("sample,sample_name,metadata_1,metadata_2,metadata_3,metadata_4,metadata_5,metadata_6,metadata_7,metadata_8,my_header")
+            assert results.text.contains("sample1,ABC,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,my_value")
+            assert results.text.contains("sample2,DEF,2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8,my_value")
+            assert results.text.contains("sample3,GHI,3.1,3.2,3.3,3.4,3.5,3.6,3.7,3.8,my_value")
+
+            // Check IRIDA Next JSON Output
+            def iridanext_json = path("$launchDir/results/iridanext.output.json").json
+            def iridanext_global = iridanext_json.files.global
+            def iridanext_metadata = iridanext_json.metadata.samples
+
+            assert iridanext_global.findAll { it.path == "transformation/results.csv" }.size() == 1
+
+            assert iridanext_metadata.size() == 3
+
+            assert iridanext_metadata.containsKey("sample1")
+            assert iridanext_metadata.sample1.size() == 1
+            assert iridanext_metadata.sample1.my_header == "my_value"
+
+            assert iridanext_metadata.containsKey("sample2")
+            assert iridanext_metadata.sample2.size() == 1
+            assert iridanext_metadata.sample2.my_header == "my_value"
+
+            assert iridanext_metadata.containsKey("sample3")
+            assert iridanext_metadata.sample3.size() == 1
+            assert iridanext_metadata.sample3.my_header == "my_value"
+        }
+    }
+
+    test("Spaces") {
+        tag "pipeline_populate"
+        tag "pipeline_populate_spaces"
+
+        when {
+            params {
+                input = "$baseDir/tests/data/samplesheets/populate/basic.csv"
+                outdir = "results"
+
+                transformation = "populate"
+                populate_header = "my header"
+                populate_value = "my value"
+            }
+        }
+
+        then {
+            assert workflow.success
+            assert path("$launchDir/results").exists()
+
+            // Check Transformation (Machine-Readable)
+            def transformation = path("$launchDir/results/transformation/transformation.csv")
+            assert transformation.exists()
+
+            assert transformation.text.contains("sample,my header")
+            assert transformation.text.contains("sample1,my value")
+            assert transformation.text.contains("sample2,my value")
+            assert transformation.text.contains("sample3,my value")
+
+            // Check Results (Human-Readable)
+            def results = path("$launchDir/results/transformation/results.csv")
+            assert results.exists()
+
+            assert results.text.contains("sample,sample_name,metadata_1,metadata_2,metadata_3,metadata_4,metadata_5,metadata_6,metadata_7,metadata_8,my header")
+            assert results.text.contains("sample1,ABC,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,my value")
+            assert results.text.contains("sample2,DEF,2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8,my value")
+            assert results.text.contains("sample3,GHI,3.1,3.2,3.3,3.4,3.5,3.6,3.7,3.8,my value")
+
+            // Check IRIDA Next JSON Output
+            def iridanext_json = path("$launchDir/results/iridanext.output.json").json
+            def iridanext_global = iridanext_json.files.global
+            def iridanext_metadata = iridanext_json.metadata.samples
+
+            assert iridanext_global.findAll { it.path == "transformation/results.csv" }.size() == 1
+
+            assert iridanext_metadata.size() == 3
+
+            assert iridanext_metadata.containsKey("sample1")
+            assert iridanext_metadata.sample1.size() == 1
+            assert iridanext_metadata.sample1."my header" == "my value"
+
+            assert iridanext_metadata.containsKey("sample2")
+            assert iridanext_metadata.sample2.size() == 1
+            assert iridanext_metadata.sample2."my header" == "my value"
+
+            assert iridanext_metadata.containsKey("sample3")
+            assert iridanext_metadata.sample3.size() == 1
+            assert iridanext_metadata.sample3."my header" == "my value"
+        }
+    }
+
+    test("Overwrite") {
+        tag "pipeline_populate"
+        tag "pipeline_populate_overwrite"
+
+        when {
+            params {
+                input = "$baseDir/tests/data/samplesheets/populate/basic.csv"
+                outdir = "results"
+
+                transformation = "populate"
+                populate_header = "metadata_2"
+                populate_value = "my_value"
+            }
+        }
+
+        then {
+            assert workflow.success
+            assert path("$launchDir/results").exists()
+
+            // Check Transformation (Machine-Readable)
+            def transformation = path("$launchDir/results/transformation/transformation.csv")
+            assert transformation.exists()
+
+            assert transformation.text.contains("sample,metadata_2")
+            assert transformation.text.contains("sample1,my_value")
+            assert transformation.text.contains("sample2,my_value")
+            assert transformation.text.contains("sample3,my_value")
+
+            // Check Results (Human-Readable)
+            def results = path("$launchDir/results/transformation/results.csv")
+            assert results.exists()
+
+            assert results.text.contains("sample,sample_name,metadata_1,metadata_2,metadata_3,metadata_4,metadata_5,metadata_6,metadata_7,metadata_8")
+            assert results.text.contains("sample1,ABC,1.1,my_value,1.3,1.4,1.5,1.6,1.7,1.8")
+            assert results.text.contains("sample2,DEF,2.1,my_value,2.3,2.4,2.5,2.6,2.7,2.8")
+            assert results.text.contains("sample3,GHI,3.1,my_value,3.3,3.4,3.5,3.6,3.7,3.8")
+
+            // Check IRIDA Next JSON Output
+            def iridanext_json = path("$launchDir/results/iridanext.output.json").json
+            def iridanext_global = iridanext_json.files.global
+            def iridanext_metadata = iridanext_json.metadata.samples
+
+            assert iridanext_global.findAll { it.path == "transformation/results.csv" }.size() == 1
+
+            assert iridanext_metadata.size() == 3
+
+            assert iridanext_metadata.containsKey("sample1")
+            assert iridanext_metadata.sample1.size() == 1
+            assert iridanext_metadata.sample1.metadata_2 == "my_value"
+
+            assert iridanext_metadata.containsKey("sample2")
+            assert iridanext_metadata.sample2.size() == 1
+            assert iridanext_metadata.sample2.metadata_2 == "my_value"
+
+            assert iridanext_metadata.containsKey("sample3")
+            assert iridanext_metadata.sample3.size() == 1
+            assert iridanext_metadata.sample3.metadata_2 == "my_value"
+        }
+    }
+
+    test("Missing metadata") {
+        tag "pipeline_populate"
+        tag "pipeline_populate_missing_metadata"
+
+        when {
+            params {
+                input = "$baseDir/tests/data/samplesheets/populate/missing_metadata.csv"
+                outdir = "results"
+
+                transformation = "populate"
+                populate_header = "my_header"
+                populate_value = "my_value"
+            }
+        }
+
+        then {
+            assert workflow.success
+            assert path("$launchDir/results").exists()
+
+            // Check Transformation (Machine-Readable)
+            def transformation = path("$launchDir/results/transformation/transformation.csv")
+            assert transformation.exists()
+
+            assert transformation.text.contains("sample,my_header")
+            assert transformation.text.contains("sample1,my_value")
+            assert transformation.text.contains("sample2,my_value")
+            assert transformation.text.contains("sample3,my_value")
+
+            // Check Results (Human-Readable)
+            def results = path("$launchDir/results/transformation/results.csv")
+            assert results.exists()
+
+            assert results.text.contains("sample,sample_name,metadata_1,metadata_2,metadata_3,metadata_4,metadata_5,metadata_6,metadata_7,metadata_8,my_header")
+            assert results.text.contains("sample1,ABC,,1.2,1.3,,1.5,1.6,,,my_value")
+            assert results.text.contains("sample2,sample2,2.1,,2.3,2.4,,2.6,2.7,,my_value")
+            assert results.text.contains("sample3,GHI,3.1,3.2,,3.4,3.5,,3.7,,my_value")
+
+            // Check IRIDA Next JSON Output
+            def iridanext_json = path("$launchDir/results/iridanext.output.json").json
+            def iridanext_global = iridanext_json.files.global
+            def iridanext_metadata = iridanext_json.metadata.samples
+
+            assert iridanext_global.findAll { it.path == "transformation/results.csv" }.size() == 1
+
+            assert iridanext_metadata.size() == 3
+
+            assert iridanext_metadata.containsKey("sample1")
+            assert iridanext_metadata.sample1.size() == 1
+            assert iridanext_metadata.sample1.my_header == "my_value"
+
+            assert iridanext_metadata.containsKey("sample2")
+            assert iridanext_metadata.sample2.size() == 1
+            assert iridanext_metadata.sample2.my_header == "my_value"
+
+            assert iridanext_metadata.containsKey("sample3")
+            assert iridanext_metadata.sample3.size() == 1
+            assert iridanext_metadata.sample3.my_header == "my_value"
+        }
+    }
+
+    test("Unrelated parameters") {
+        tag "pipeline_populate"
+        tag "pipeline_populate_unrelated_parameters"
+
+        when {
+            params {
+                input = "$baseDir/tests/data/samplesheets/populate/basic.csv"
+                outdir = "results"
+
+                transformation = "populate"
+                populate_header = "my_header"
+                populate_value = "my_value"
+
+                age_header = "no_effect"
+                earliest_header = "also_no_effect"
+            }
+        }
+
+        then {
+            assert workflow.success
+            assert path("$launchDir/results").exists()
+
+            // Check Transformation (Machine-Readable)
+            def transformation = path("$launchDir/results/transformation/transformation.csv")
+            assert transformation.exists()
+
+            assert transformation.text.contains("sample,my_header")
+            assert transformation.text.contains("sample1,my_value")
+            assert transformation.text.contains("sample2,my_value")
+            assert transformation.text.contains("sample3,my_value")
+
+            // Check Results (Human-Readable)
+            def results = path("$launchDir/results/transformation/results.csv")
+            assert results.exists()
+
+            assert results.text.contains("sample,sample_name,metadata_1,metadata_2,metadata_3,metadata_4,metadata_5,metadata_6,metadata_7,metadata_8,my_header")
+            assert results.text.contains("sample1,ABC,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,my_value")
+            assert results.text.contains("sample2,DEF,2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8,my_value")
+            assert results.text.contains("sample3,GHI,3.1,3.2,3.3,3.4,3.5,3.6,3.7,3.8,my_value")
+
+            // Check IRIDA Next JSON Output
+            def iridanext_json = path("$launchDir/results/iridanext.output.json").json
+            def iridanext_global = iridanext_json.files.global
+            def iridanext_metadata = iridanext_json.metadata.samples
+
+            assert iridanext_global.findAll { it.path == "transformation/results.csv" }.size() == 1
+
+            assert iridanext_metadata.size() == 3
+
+            assert iridanext_metadata.containsKey("sample1")
+            assert iridanext_metadata.sample1.size() == 1
+            assert iridanext_metadata.sample1.my_header == "my_value"
+
+            assert iridanext_metadata.containsKey("sample2")
+            assert iridanext_metadata.sample2.size() == 1
+            assert iridanext_metadata.sample2.my_header == "my_value"
+
+            assert iridanext_metadata.containsKey("sample3")
+            assert iridanext_metadata.sample3.size() == 1
+            assert iridanext_metadata.sample3.my_header == "my_value"
+        }
+    }
+}
diff --git a/workflows/metadatatransformation.nf b/workflows/metadatatransformation.nf
index c88803b..2d5cd20 100644
--- a/workflows/metadatatransformation.nf
+++ b/workflows/metadatatransformation.nf
@@ -97,7 +97,7 @@ workflow METADATATRANSFORMATION {
     }.toList()
 
     // TRANSFORM METADATA
-    valid_transformations = ['lock', 'age', 'earliest']
+    valid_transformations = ['lock', 'age', 'earliest', 'populate']
 
     if(valid_transformations.contains(params.transformation)) {
         write_metadata = WRITE_METADATA (metadata_headers, metadata_rows)


Reviews:
user: login: emarinier
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-12T17:47:42Z
user: login: sgsutcliffe
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T18:15:27Z
user: login: apetkau
body: Awesome Eric. Thanks so much for this 😄 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-12T18:19:17Z
user: login: emarinier
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-12T20:32:53Z

Review comments:
diff_hunk: @@ -78,9 +79,15 @@ The following parameters can be used to rename CSV-generated output columns as f
 - `--metadata_6_header`: names the first metadata_6 column header
 - `--metadata_7_header`: names the first metadata_7 column header
 - `--metadata_8_header`: names the first metadata_8 column header
+- `--earliest_header`: names the earliest date column header and related output columns
path: README.md
user: login: emarinier
body: Updated this missing parameter for the earliest transformation, as I noticed it was missing.
created_at: 2025-03-12T17:47:42Z
author_association: MEMBER
diff_hunk: @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### `Added`
 
 - `earliest` transformation: finds earliest date among metadata
+- `populate` transformation: populates an output column with a specific value
path: CHANGELOG.md
user: login: apetkau
body: For this statement and the `earliest` one, can you reference the PR where this function was added. E.g., [PR #10](https://github.com/phac-nml/metadatatransformation/pull/10)
created_at: 2025-03-12T18:19:05Z
author_association: MEMBER
diff_hunk: @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### `Added`
 
 - `earliest` transformation: finds earliest date among metadata
+- `populate` transformation: populates an output column with a specific value
path: CHANGELOG.md
user: login: emarinier
body: Add in [42582be](https://github.com/phac-nml/metadatatransformation/pull/10/commits/42582be3232ee5decd1b6510cf929a8b83e3e9bf) and [4fa4a36](https://github.com/phac-nml/metadatatransformation/pull/10/commits/4fa4a3652ca8c85c4ce8bbd6853e27984d0a7135)
created_at: 2025-03-12T20:32:53Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-12T17:48:07Z
body: ## `nf-core pipelines lint` overall result: Passed :white_check_mark: :warning:

Posted for pipeline commit 4fa4a36

```diff
+| ✅ 145 tests passed       |+
#| ❔  29 tests were ignored |#
!| ❗   1 tests had warnings |!
```

<details>

### :heavy_exclamation_mark: Test warnings:

* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - nf-validation has been detected in the pipeline. Please migrate to nf-schema: https://nextflow-io.github.io/nf-schema/latest/migration_guide/

### :grey_question: Tests ignored:

* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File is ignored: `assets/nf-core-metadatatransformation_logo_light.png`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File is ignored: `docs/images/nf-core-metadatatransformation_logo_light.png`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File is ignored: `docs/images/nf-core-metadatatransformation_logo_dark.png`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File is ignored: `.github/workflows/awstest.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File is ignored: `.github/workflows/awsfulltest.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File is ignored: `lib/Utils.groovy`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File is ignored: `lib/WorkflowMain.groovy`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File is ignored: `lib/NfcoreTemplate.groovy`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File is ignored: `lib/WorkflowMetadatatransformation.groovy`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File is ignored: `conf/igenomes_ignored.config`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File is ignored: `ro-crate-metadata.json`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable ignored: `manifest.name`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable ignored: `manifest.homePage`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable ignored: `params.max_cpus`
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - File ignored due to lint config: `LICENSE` or `LICENSE.md` or `LICENCE` or `LICENCE.md`
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - File ignored due to lint config: `.github/CONTRIBUTING.md`
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - File ignored due to lint config: `.github/ISSUE_TEMPLATE/bug_report.yml`
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - File ignored due to lint config: `.github/PULL_REQUEST_TEMPLATE.md`
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - File ignored due to lint config: `.github/workflows/branch.yml`
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - File ignored due to lint config: `assets/email_template.html`
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - File ignored due to lint config: `assets/email_template.txt`
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - File ignored due to lint config: `assets/sendmail_template.txt`
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - File does not exist: `assets/nf-core-metadatatransformation_logo_light.png`
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - File does not exist: `docs/images/nf-core-metadatatransformation_logo_light.png`
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - File does not exist: `docs/images/nf-core-metadatatransformation_logo_dark.png`
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - File ignored due to lint config: `docs/README.md`
* [actions_awstest](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/actions_awstest) - 'awstest.yml' workflow not found: `/home/runner/work/metadatatransformation/metadatatransformation/.github/workflows/awstest.yml`
* [actions_awsfulltest](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/actions_awsfulltest) - actions_awsfulltest
* [pipeline_name_conventions](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/pipeline_name_conventions) - pipeline_name_conventions

### :white_check_mark: Tests passed:

* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.gitattributes`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.gitignore`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.nf-core.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.editorconfig`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.prettierignore`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.prettierrc.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `CHANGELOG.md`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `CITATIONS.md`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `CODE_OF_CONDUCT.md`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `LICENSE` or `LICENSE.md` or `LICENCE` or `LICENCE.md`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `nextflow_schema.json`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `nextflow.config`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `README.md`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.github/.dockstore.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.github/CONTRIBUTING.md`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.github/ISSUE_TEMPLATE/bug_report.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.github/ISSUE_TEMPLATE/config.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.github/ISSUE_TEMPLATE/feature_request.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.github/PULL_REQUEST_TEMPLATE.md`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.github/workflows/branch.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.github/workflows/ci.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.github/workflows/linting_comment.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `.github/workflows/linting.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `assets/email_template.html`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `assets/email_template.txt`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `assets/sendmail_template.txt`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `conf/modules.config`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `conf/test.config`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `conf/test_full.config`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `docs/output.md`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `docs/README.md`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `docs/README.md`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `docs/usage.md`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `main.nf`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `assets/multiqc_config.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `conf/base.config`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `conf/igenomes.config`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File found: `modules.json`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `.github/ISSUE_TEMPLATE/bug_report.md`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `.github/ISSUE_TEMPLATE/feature_request.md`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `.github/workflows/push_dockerhub.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `.markdownlint.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `.nf-core.yaml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `.yamllint.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `bin/markdown_to_html.r`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `conf/aws.config`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `docs/images/nf-core-metadatatransformation_logo.png`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `lib/Checks.groovy`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `lib/Completion.groovy`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `lib/Workflow.groovy`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `parameters.settings.json`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `pipeline_template.yml`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `Singularity`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `lib/nfcore_external_java_deps.jar`
* [files_exist](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_exist) - File not found check: `.travis.yml`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Found nf-validation plugin
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `manifest.nextflowVersion`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `manifest.description`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `manifest.version`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `timeline.enabled`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `trace.enabled`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `report.enabled`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `dag.enabled`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `process.cpus`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `process.memory`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `process.time`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `params.outdir`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `params.input`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `manifest.mainScript`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `timeline.file`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `trace.file`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `report.file`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable found: `dag.file`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable (correctly) not found: `params.nf_required_version`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable (correctly) not found: `params.container`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable (correctly) not found: `params.singleEnd`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable (correctly) not found: `params.igenomesIgnore`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable (correctly) not found: `params.name`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable (correctly) not found: `params.enable_conda`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config ``timeline.enabled`` had correct value: ``true``
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config ``report.enabled`` had correct value: ``true``
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config ``trace.enabled`` had correct value: ``true``
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config ``dag.enabled`` had correct value: ``true``
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config ``dag.file`` ended with ``.html``
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config variable ``manifest.nextflowVersion`` started with >= or !>=
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config ``manifest.version`` ends in ``dev``: ``1.1.0dev``
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - nextflow.config contains configuration profile `test`
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.transformation= lock
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.age_header= age
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.earliest_header= earliest_date
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.populate_header= populated_header
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.populate_value= populated_value
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.metadata_1_header= metadata_1
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.metadata_2_header= metadata_2
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.metadata_3_header= metadata_3
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.metadata_4_header= metadata_4
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.metadata_5_header= metadata_5
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.metadata_6_header= metadata_6
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.metadata_7_header= metadata_7
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.metadata_8_header= metadata_8
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.max_cpus= 4
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.max_memory= 2.GB
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.max_time= 1.h
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.publish_dir_mode= copy
* [nextflow_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nextflow_config) - Config default value correct: params.validate_params= true
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - `.gitattributes` matches the template
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - `.prettierrc.yml` matches the template
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - `.github/.dockstore.yml` matches the template
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - `.github/ISSUE_TEMPLATE/feature_request.yml` matches the template
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - `.github/workflows/linting_comment.yml` matches the template
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - `.github/workflows/linting.yml` matches the template
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - `.gitignore` matches the template
* [files_unchanged](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/files_unchanged) - `.prettierignore` matches the template
* [actions_ci](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/actions_ci) - '.github/workflows/ci.yml' is triggered on expected events
* [actions_ci](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/actions_ci) - '.github/workflows/ci.yml' checks minimum NF version
* [readme](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/readme) - README Zenodo placeholder was replaced with DOI.
* [pipeline_todos](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/pipeline_todos) - No TODO strings found
* [plugin_includes](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/plugin_includes) - No wrong validation plugin imports have been found
* [template_strings](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/template_strings) - Did not find any Jinja template strings (0 files)
* [schema_lint](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/schema_lint) - Schema lint passed
* [schema_lint](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/schema_lint) - Schema title + description lint passed
* [schema_lint](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/schema_lint) - Input mimetype lint passed: 'text/csv'
* [schema_params](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/schema_params) - Schema matched params returned from nextflow config
* [system_exit](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/system_exit) - No `System.exit` calls found
* [actions_schema_validation](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/actions_schema_validation) - Workflow validation passed: ci.yml
* [actions_schema_validation](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/actions_schema_validation) - Workflow validation passed: branch.yml
* [actions_schema_validation](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/actions_schema_validation) - Workflow validation passed: linting_comment.yml
* [actions_schema_validation](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/actions_schema_validation) - Workflow validation passed: linting.yml
* [merge_markers](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/merge_markers) - No merge markers found in pipeline files
* [modules_json](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/modules_json) - Only installed modules found in `modules.json`
* [multiqc_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/multiqc_config) - `assets/multiqc_config.yml` found and not ignored.
* [multiqc_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/multiqc_config) - `assets/multiqc_config.yml` contains `report_section_order`
* [multiqc_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/multiqc_config) - `assets/multiqc_config.yml` contains `export_plots`
* [multiqc_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/multiqc_config) - `assets/multiqc_config.yml` contains `report_comment`
* [multiqc_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/multiqc_config) - `assets/multiqc_config.yml` follows the ordering scheme of the minimally required plugins.
* [multiqc_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/multiqc_config) - `assets/multiqc_config.yml` contains 'export_plots: true'.
* [modules_structure](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/modules_structure) - modules directory structure is correct 'modules/nf-core/TOOL/SUBTOOL'
* [local_component_structure](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/local_component_structure) - local subworkflows directory structure is correct 'subworkflows/local/TOOL/SUBTOOL'
* [base_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/base_config) - `conf/base.config` found and not ignored.
* [base_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/base_config) - `CUSTOM_DUMPSOFTWAREVERSIONS` found in `conf/base.config` and Nextflow scripts.
* [modules_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/modules_config) - `conf/modules.config` found and not ignored.
* [modules_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/modules_config) - `CUSTOM_DUMPSOFTWAREVERSIONS` found in `conf/modules.config` and Nextflow scripts.
* [modules_config](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/modules_config) - `TRANSFORM_METADATA` found in `conf/modules.config` and Nextflow scripts.
* [nfcore_yml](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nfcore_yml) - Repository type in `.nf-core.yml` is valid: `pipeline`
* [nfcore_yml](https://nf-co.re/tools/docs/3.2.0/pipeline_lint_tests/nfcore_yml) - nf-core version in `.nf-core.yml` is set to the latest version: `3.2.0`

### Run details

* nf-core/tools version 3.2.0
* Run at `2025-03-12 20:39:34`

</details>

<!-- Sticky Pull Request Comment -->
author_association: NONE
user: login: sgsutcliffe
created_at: 2025-03-12T18:15:16Z
body: No comments! Looks good
author_association: CONTRIBUTOR