-
Notifications
You must be signed in to change notification settings - Fork 178
/
multiqc_config.py
130 lines (108 loc) · 6.16 KB
/
multiqc_config.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
from pathlib import Path
from typing import Dict, List
import yaml
from nf_core.lint_utils import ignore_file
def multiqc_config(self) -> Dict[str, List[str]]:
"""Make sure basic multiQC plugins are installed and plots are exported
Basic template:
.. code-block:: yaml
report_comment: >
This report has been generated by the <a href="https://github.com/nf-core/quantms" target="_blank">nf-core/quantms</a>
analysis pipeline. For information about how to interpret these results, please see the
<a href="https://nf-co.re/quantms" target="_blank">documentation</a>.
report_section_order:
software_versions:
order: -1000
nf-core-quantms-summary:
order: -1001
export_plots: true
.. note:: You can choose to ignore this lint tests by editing the file called
``.nf-core.yml`` in the root of your pipeline and setting the test to false:
.. code-block:: yaml
lint:
multiqc_config: False
"""
passed: List[str] = []
failed: List[str] = []
ignored: List[str] = []
fn = Path(self.wf_path, "assets", "multiqc_config.yml")
file_path = fn.relative_to(self.wf_path)
passed, failed, ignored, ignore_configs = ignore_file("multiqc_config", file_path, self.wf_path)
# skip other tests if the file is not found
error_message = f"`{file_path}` not found"
# check for partial match in failed or ignored
if not any(f.startswith(error_message) for f in (failed + ignored)):
try:
with open(fn) as fh:
mqc_yml = yaml.safe_load(fh)
except Exception as e:
return {"failed": [f"Could not parse yaml file: {fn}, {e}"]}
# check if required sections are present
required_sections = ["report_section_order", "export_plots", "report_comment"]
for section in required_sections:
if section not in mqc_yml and section not in ignore_configs:
failed.append(f"`assets/multiqc_config.yml` does not contain `{section}`")
return {"passed": passed, "failed": failed}
else:
passed.append(f"`assets/multiqc_config.yml` contains `{section}`")
try:
orders = {}
summary_plugin_name = f"{self.pipeline_prefix}-{self.pipeline_name}-summary"
min_plugins = ["software_versions", summary_plugin_name]
for plugin in min_plugins:
if plugin not in mqc_yml["report_section_order"]:
raise AssertionError(f"Section {plugin} missing in report_section_order")
if "order" not in mqc_yml["report_section_order"][plugin]:
raise AssertionError(f"Section {plugin} 'order' missing. Must be < 0")
plugin_order = mqc_yml["report_section_order"][plugin]["order"]
if plugin_order >= 0:
raise AssertionError(f"Section {plugin} 'order' must be < 0")
for plugin in mqc_yml["report_section_order"]:
if "order" in mqc_yml["report_section_order"][plugin]:
orders[plugin] = mqc_yml["report_section_order"][plugin]["order"]
if orders[summary_plugin_name] != min(orders.values()):
raise AssertionError(f"Section {summary_plugin_name} should have the lowest order")
orders.pop(summary_plugin_name)
if orders["software_versions"] != min(orders.values()):
raise AssertionError("Section software_versions should have the second lowest order")
except (AssertionError, KeyError, TypeError) as e:
failed.append(f"`assets/multiqc_config.yml` does not meet requirements: {e}")
else:
passed.append("`assets/multiqc_config.yml` follows the ordering scheme of the minimally required plugins.")
if "report_comment" not in ignore_configs:
# Check that the minimum plugins exist and are coming first in the summary
version = self.nf_config.get("manifest.version", "").strip(" '\"")
if "dev" in version:
version = "dev"
report_comments = (
f'This report has been generated by the <a href="https://github.com/nf-core/{self.pipeline_name}/tree/dev" target="_blank">nf-core/{self.pipeline_name}</a>'
f" analysis pipeline. For information about how to interpret these results, please see the "
f'<a href="https://nf-co.re/{self.pipeline_name}/dev/docs/output" target="_blank">documentation</a>.'
)
else:
report_comments = (
f'This report has been generated by the <a href="https://github.com/nf-core/{self.pipeline_name}/releases/tag/{version}" target="_blank">nf-core/{self.pipeline_name}</a>'
f" analysis pipeline. For information about how to interpret these results, please see the "
f'<a href="https://nf-co.re/{self.pipeline_name}/{version}/docs/output" target="_blank">documentation</a>.'
)
if mqc_yml["report_comment"].strip() != report_comments:
# find where the report_comment is wrong and give it as a hint
hint = report_comments
failed.append(
f"`assets/multiqc_config.yml` does not contain a matching 'report_comment'. \n"
f"The expected comment is: \n"
f"```{hint}``` \n"
f"The current comment is: \n"
f"```{ mqc_yml['report_comment'].strip()}```"
)
else:
passed.append("`assets/multiqc_config.yml` contains a matching 'report_comment'.")
# Check that export_plots is activated
try:
if not mqc_yml["export_plots"]:
raise AssertionError()
except (AssertionError, KeyError, TypeError):
failed.append("`assets/multiqc_config.yml` does not contain 'export_plots: true'.")
else:
passed.append("`assets/multiqc_config.yml` contains 'export_plots: true'.")
return {"passed": passed, "failed": failed, "ignored": ignored}