# Buggy code
def write_data_file(data, fn, sort_cols=False, data_format=None):
    """Write a data file to the report directory. Will not do anything
    if config.data_dir is not set.
    :param: data - a 2D dict, first key sample name (row header),
            second key field (column header).
    :param: fn - Desired filename. Directory will be prepended automatically.
    :param: sort_cols - Sort columns alphabetically
    :param: data_format - Output format. Defaults to config.data_format (usually tsv)
    :return: None"""

    if config.data_dir is not None:
        # Add relevant file extension to filename
        if data_format is None:
            data_format = config.data_format
        fn = "{}.{}".format(fn, config.data_format_extensions[data_format])

        # Save file
        with io.open(os.path.join(config.data_dir, fn), "w", encoding="utf-8") as f:
            if data_format == "json":
                jsonstr = json.dumps(data, indent=4, ensure_ascii=False)
                print(jsonstr.encode("utf-8", "ignore").decode("utf-8"), file=f)
            elif data_format == "yaml":
                yaml.dump(data, f, default_flow_style=False)
            else:
                # Default - tab separated output
                # Get all headers
                h = ["Sample"]
                for sn in sorted(data.keys()):
                    for k in data[sn].keys():
                        if type(data[sn][k]) is not dict and k not in h:
                            h.append(str(k))
                if sort_cols:
                    h = sorted(h)

                # Get the rows
                rows = ["\t".join(h)]
                for sn in sorted(data.keys()):
                    # Make a list starting with the sample name, then each field in order of the header cols
                    l = [sn] + [str(data[sn].get(k, "")) for k in h[1:]]
                    rows.append("\t".join(l))

                body = "\n".join(rows)

                print(body.encode("utf-8", "ignore").decode("utf-8"), file=f)


# Traceback (TypeError)
[INFO   ]         multiqc : This is MultiQC v1.2.dev0 (e86ba77)
[INFO   ]         multiqc : Template    : default
[INFO   ]         multiqc : Searching '.'
Searching 3 files..  [####################################]  100%
[ERROR  ]         multiqc : Oops! The 'bcl2fastq' MultiQC module broke...
Please copy the following traceback and report it at https://github.com/ewels/MultiQC/issues
(if possible, include a log file that triggers the error)
============================================================
Module bcl2fastq raised an exception: Traceback (most recent call last):
File "/tank/home/s216121/software/MultiQC/scripts/multiqc", line 365, in multiqc
output = mod()
File "/tank/home/s216121/software/MultiQC/multiqc/modules/bcl2fastq/bcl2fastq.py", line 88, in __init__
self.write_data_file(data, 'multiqc_mymod')
File "/tank/home/s216121/software/MultiQC/multiqc/modules/base_module.py", line 267, in write_data_file
util_functions.write_data_file(data, fn, sort_cols, data_format)
File "/tank/home/s216121/software/MultiQC/multiqc/utils/util_functions.py", line 80, in write_data_file
rows.append( "\t".join(l) )
TypeError: sequence item 0: expected str instance, int found
============================================================
[WARNING]         multiqc : No analysis results found. Cleaning up..
[INFO   ]         multiqc : MultiQC complete

# Fixed code
def write_data_file(data, fn, sort_cols=False, data_format=None):
    """Write a data file to the report directory. Will not do anything
    if config.data_dir is not set.
    :param: data - a 2D dict, first key sample name (row header),
            second key field (column header).
    :param: fn - Desired filename. Directory will be prepended automatically.
    :param: sort_cols - Sort columns alphabetically
    :param: data_format - Output format. Defaults to config.data_format (usually tsv)
    :return: None"""

    if config.data_dir is not None:
        # Add relevant file extension to filename
        if data_format is None:
            data_format = config.data_format
        fn = "{}.{}".format(fn, config.data_format_extensions[data_format])

        # Save file
        with io.open(os.path.join(config.data_dir, fn), "w", encoding="utf-8") as f:
            if data_format == "json":
                jsonstr = json.dumps(data, indent=4, ensure_ascii=False)
                print(jsonstr.encode("utf-8", "ignore").decode("utf-8"), file=f)
            elif data_format == "yaml":
                yaml.dump(data, f, default_flow_style=False)
            else:
                # Default - tab separated output
                # Get all headers
                h = ["Sample"]
                for sn in sorted(data.keys()):
                    for k in data[sn].keys():
                        if type(data[sn][k]) is not dict and k not in h:
                            h.append(str(k))
                if sort_cols:
                    h = sorted(h)

                # Get the rows
                rows = ["\t".join(h)]
                for sn in sorted(data.keys()):
                    # Make a list starting with the sample name, then each field in order of the header cols
                    l = [str(sn)] + [str(data[sn].get(k, "")) for k in h[1:]]
                    rows.append("\t".join(l))

                body = "\n".join(rows)

                print(body.encode("utf-8", "ignore").decode("utf-8"), file=f)