Repository 'mcdoe'
hg clone https://toolshed.g2.bx.psu.edu/repos/padge/mcdoe

Changeset 0:cc0957c46408 (2022-05-12)
Next changeset 1:4a5c94d1d8bb (2022-08-31)
Commit message:
"planemo upload for repository https://github.com/kirstvh/MultiplexCrisprDOE commit b6c1b1860eee82b06ed4a592d1f9eee6886be318-dirty"
added:
MultiplexCrisprDOE.jl
README.rst
main.jl
mcdoe.xml
report.jmd
test-data/example_data.xlsx
test-data/test_ccp_report.html
test-data/test_countKOs.xlsx
test-data/test_gRNA_edit.xlsx
test-data/test_gRNA_reads.xlsx
test-data/test_ged_report.html
test-data/test_gfd_report.html
test-data/test_sim_report.html
b
diff -r 000000000000 -r cc0957c46408 MultiplexCrisprDOE.jl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MultiplexCrisprDOE.jl Thu May 12 17:39:18 2022 +0000
[
b'@@ -0,0 +1,1048 @@\n+"""\n+    gRNA_frequency_distribution(m, \n+                                sd, \n+                                l, \n+                                u, \n+                                n_gRNA_total; \n+                                normalize = true, \n+                                visualize = false)\n+\n+Generates vector with frequencies in the combinatorial gRNA/Cas9 construct library for all gRNAs\n+\n+***INPUT***\n+m: the average abundance of the gRNAs (in terms of absolute or relative frequency)\n+sd: the standard deviation on the gRNA abundances (in terms of absolute or relative frequency)\n+l: minimal gRNA abundance (in terms of absolute or relative frequency)\n+u: maximal gRNA abundance (in terms of absolute or relative frequency)\n+n_gRNA_total: the total number of gRNAs in the experiment\n+normalize: if set to "true", the gRNA abundances (absolute frequencies) are converted into relative frequencies\n+visualize: if set to "true", a histogram of all gRNA abundances is plotted\n+\n+***OUTPUT***\n+p_gRNA_freq: vector with frequencies for all gRNAs in the construct library\n+"""\n+function gRNA_frequency_distribution(m, \n+                                    sd, \n+                                    l, \n+                                    u, \n+                                    n_gRNA_total; \n+                                    normalize = true, \n+                                    visualize = false)\n+\n+    d_gRNA_freq = truncated(Normal(m, sd), l, u)  # gRNA frequency distribution\n+    p_gRNA_freq = collect(rand(d_gRNA_freq, n_gRNA_total))  # sample gRNA frequencies from distribution\n+    \n+    if normalize # convert into relative frequencies\n+        p_gRNA_freq /= sum(p_gRNA_freq)\n+    end\n+\n+    if visualize\n+        return histogram(p_gRNA_freq, label="", \n+            xlabel="Number of reads per gRNA", \n+            linecolor="white", \n+            normalize=:probability,\n+            xtickfontsize=10,ytickfontsize=10,\n+            color=:mediumturquoise, size=(600,350), bins = 25,\n+            ylabel="Relative frequency", \n+            title="gRNA frequency distribution")\n+    else\n+        return p_gRNA_freq\n+    end\n+end\n+\n+"""\n+    gRNA_edit_distribution(f_act, \n+                            \xcf\xb5_edit_act, \n+                            \xcf\xb5_edit_inact, \n+                            sd_act, \n+                            n_gRNA_total; \n+                            visualize = false)   \n+\n+Generates vector with genome editing efficiencies for all the gRNAs in the experiment. \n+\n+***INPUT***\n+f_act: fraction of all gRNAs that is active\n+\xcf\xb5_edit_act: Average genome editing efficiency for active gRNAs - mean of the genome editing efficiency distribution for active gRNAs\n+\xcf\xb5_edit_inact: Average genome editing efficiency for inactive gRNAs - mean of the genome editing efficiency distribution for inactive gRNAs\n+sd_act: standard deviation of the genome editing efficiency distributions for active and inactive gRNAs\n+n_gRNA_total: the total number of gRNAs in the experiment\n+visualize: if set to "true", a histogram of all genome editing efficiency is plotted\n+\n+***OUTPUT***\n+p_gRNA_edit: vector with genome editing efficiencies for all gRNAs\n+"""\n+function gRNA_edit_distribution(f_act, \xcf\xb5_edit_act, \xcf\xb5_edit_inact, sd_act, n_gRNA_total; visualize=false)   \n+    d_act = Binomial(1, f_act) # there is a probability f_act that a gRNA is active\n+    d_high_act = truncated(Normal(\xcf\xb5_edit_act, sd_act), 0.01, 1)  # average genome editing efficiency for active gRNAs is equal to \xcf\xb5_edit_act\n+    d_low_act = truncated(Normal(\xcf\xb5_edit_inact, sd_act), 0.01, 1) # average genome editing efficiency for inactive gRNAs is equal to \xcf\xb5_edit_inact\n+    p_gRNA_edit = zeros(n_gRNA_total) # initialize vector with genome editing efficiencies for gRNAs\n+    \n+    for i in 1:n_gRNA_total\n+        if rand(d_act, 1) == [1]  # gRNA is active\n+            p_gRNA_edit[i] = rand(d_high_act, 1)[1]\n+        else  # gRNA is inactive\n+            p_gRNA_ed'..b'  p_gRNA_freq, \n+                p_gRNA_edit, \n+                \xcf\xb5_KO)\n+\n+Computes the probability of full coverage of all triple combinations of gene knockouts (Px,3) for an experiment with given plant library size using BioCCP\n+\n+***INPUT***\n+x: number of target genes in the experiment\n+N: plant library size\n+g: number of gRNAs designed per target gene\n+r: number of gRNA sequences per combinatorial gRNA/Cas construct\n+n_gRNA_total: total number of gRNAs in the experiment\n+p_gRNA_freq: vector with relative frequencies for all gRNAs in the construct library (normalized!)\n+p_gRNA_edit: vector with genome editing efficiencies for all gRNAs \n+\xcf\xb5_KO: global knockout efficiency; fraction of mutations leading to effective gene knockout\n+                \n+***OUTPUT***\n+P\xe2\x82\x93\xe2\x82\x83: probability of full coverage of all triple combinations of gene knockouts\n+"""\n+function BioCCP_P\xe2\x82\x93\xe2\x82\x83(x, \n+                N,\n+                g, \n+                r, \n+                n_gRNA_total, \n+                p_gRNA_freq, \n+                p_gRNA_edit, \n+                \xcf\xb5_KO)\n+    \n+    # how many triple combinations of gRNAs\n+    ind_combinations_gRNA = collect(combinations(1:n_gRNA_total, 3))\n+    n_combinations_gRNA = length(ind_combinations_gRNA)\n+    \n+    # calculate probability and activity of triple gRNA combinations\n+    p_combinations_gRNA_library = zeros(n_combinations_gRNA)\n+    p_combinations_gRNA_act = zeros(n_combinations_gRNA)\n+    for i in 1:n_combinations_gRNA\n+        p_combinations_gRNA_library[i] = p_gRNA_freq[ind_combinations_gRNA[i][1]] * p_gRNA_freq[ind_combinations_gRNA[i][2]] * p_gRNA_freq[ind_combinations_gRNA[i][3]]\n+        p_combinations_gRNA_act[i] = p_gRNA_edit[ind_combinations_gRNA[i][1]] * p_gRNA_edit[ind_combinations_gRNA[i][2]] * p_gRNA_edit[ind_combinations_gRNA[i][3]]\n+    end\n+    \n+    # normalize probability gRNA combinations\n+    p_combinations_gRNA_library /= sum(p_combinations_gRNA_library)\n+\n+    # select triple gRNA combinations of which each component codes for different gene (goal is to study combinations of knockouts in different genes)\n+    p_combinations_gRNA_library_interest = []\n+    p_combinations_gRNA_act_interest = []\n+    ind_combinations_gRNA_interest = []\n+    for i in 1:n_combinations_gRNA\n+        if ceil(ind_combinations_gRNA[i][1]/g) != ceil(ind_combinations_gRNA[i][2]/g) && ceil(ind_combinations_gRNA[i][1]/g) != ceil(ind_combinations_gRNA[i][3]/g) && ceil(ind_combinations_gRNA[i][3]/g) != ceil(ind_combinations_gRNA[i][2]/g)\n+            push!(p_combinations_gRNA_library_interest, p_combinations_gRNA_library[i])\n+            push!(p_combinations_gRNA_act_interest, p_combinations_gRNA_act[i])\n+            push!(ind_combinations_gRNA_interest, ind_combinations_gRNA[i])\n+        end\n+    end\n+        \n+    n_combinations_gRNA_interest = length(p_combinations_gRNA_library_interest)\n+    p_combinations_gRNA = p_combinations_gRNA_library_interest .* p_combinations_gRNA_act_interest * \xcf\xb5_KO^3\n+\n+    # sum up probabilities or gRNA combinations for corresponding gene knockout combinations\n+    p_genes_matrix = zeros(x, x, x)\n+    for i in 1:n_combinations_gRNA_interest\n+        gene1 = Int(ceil(ind_combinations_gRNA_interest[i][1]/g))\n+        gene2 = Int(ceil(ind_combinations_gRNA_interest[i][2]/g))\n+        gene3 = Int(ceil(ind_combinations_gRNA_interest[i][3]/g))\n+        p_genes_matrix[gene1, gene2, gene3] += p_combinations_gRNA[i]\n+    end\n+    \n+    combinations_genes = collect(combinations(1:x, 3))\n+    p_genes = []\n+        for combination in combinations_genes\n+            push!(p_genes, p_genes_matrix[combination[1], combination[2], combination[3]])\n+        end\n+        \n+    n_combinations_genes = length(p_genes)\n+    combinations_pp = length(collect(combinations(1:r, 3)))\n+    \n+    # Apply BioCCP function\n+    P\xe2\x82\x93\xe2\x82\x83 = success_probability(n_combinations_genes, N; p=p_genes, r=combinations_pp, normalize=false)\n+\n+    return P\xe2\x82\x93\xe2\x82\x83 \n+end\n+      \n+      \n\\ No newline at end of file\n'
b
diff -r 000000000000 -r cc0957c46408 README.rst
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/README.rst Thu May 12 17:39:18 2022 +0000
b
@@ -0,0 +1,4 @@
+MultiplexCrisprDOE
+==================
+provides simulation- and BioCCP-based approaches for computing the minimal plant library size 
+that guarantees full combinatorial coverage (and other relevant statistics) for multiplex CRISPR/Cas experiments in plants.
\ No newline at end of file
b
diff -r 000000000000 -r cc0957c46408 main.jl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.jl Thu May 12 17:39:18 2022 +0000
[
b'@@ -0,0 +1,612 @@\n+using Pkg\n+Pkg.add("Plots");\n+Pkg.add("Distributions");\n+Pkg.add("LinearAlgebra");\n+Pkg.add("Combinatorics");\n+Pkg.add("BioCCP");\n+Pkg.add("ArgParse");\n+Pkg.add("XLSX");\n+Pkg.add("DataFrames");\n+Pkg.add("Weave");\n+Pkg.add("DataStructures");\n+Pkg.add("PrettyTables");\n+\n+using Random\n+using Plots\n+using Distributions\n+using LinearAlgebra\n+using Combinatorics\n+using BioCCP\n+using ArgParse\n+using XLSX\n+using DataFrames\n+using Weave\n+using DataStructures\n+using PrettyTables\n+\n+global current_dir = pwd()\n+include("MultiplexCrisprDOE.jl");\n+\n+function main(args)\n+\n+    aps = ArgParseSettings("MultiplexCrisprDOE")\n+\n+    @add_arg_table! aps begin\n+        "gfd" #, "gRNA_freq_dist"\n+            action = :command        # adds a command which will be read from an argument\n+            help = "gRNA/Cas9 frequencies"\n+        "ged" #, "gRNA_edit_dist" \n+            action = :command\n+            help = "gRNA/Cas9 editing efficiencies"\n+        "sim"  # simulation\n+            action = :command\n+            help = "simulation-based approaches for computing the minimal plant library size that guarantees full combinatorial coverage (and other relevant statistics)"\n+        "ccp" # bioccp\n+            action = :command\n+            help = "BioCCP-based approaches for computing the minimal plant library size that guarantees full combinatorial coverage (and other relevant statistics)"\n+    end\n+\n+    @add_arg_table! aps["gfd"] begin    # add command arg_table: same as usual, but invoked on s["grna"]\n+        "m"\n+            arg_type = Int\n+            help = "plant library size"\n+        "sd"\n+            arg_type = Int\n+            help = "the standard deviation on the gRNA abundances (in terms of absolute or relative frequency)"\n+        "l"\n+            arg_type = Int\n+            help = "minimal gRNA abundance (in terms of absolute or relative frequency)"\n+        "u"\n+            arg_type = Int\n+            help = "maximal gRNA abundance (in terms of absolute or relative frequency)"\n+        "n" #, "--n_gRNA_total"\n+            arg_type = Int\n+            help = "the total number of gRNAs in the experiment"\n+        "--normalize"\n+            action = :store_true\n+            # arg_type = Bool\n+            # default = true\n+            help = "if provided, the gRNA abundances (absolute frequencies) are converted into relative frequencies"\n+        "--visualize"\n+            action = :store_true\n+            # arg_type = Bool\n+            # default = false\n+            help = "if provided, a histogram of all gRNA abundances is plotted"\n+        "--out_file"\n+            arg_type = String\n+            default = "gRNA_reads"\n+            help = "Output excel file prefix"\n+    end\n+\n+    @add_arg_table! aps["ged"] begin    # add command arg_table: same as usual, but invoked on s["grna"]\n+        "f_act"\n+            arg_type = Float16\n+            help = "fraction of all gRNAs that is active"\n+        "eps_edit_act"\n+            arg_type = Float16\n+            help = "Average genome editing efficiency for active gRNAs - mean of the genome editing efficiency distribution for active gRNAs"\n+        "eps_edit_inact"\n+            arg_type = Float16\n+            help = "Average genome editing efficiency for inactive gRNAs - mean of the genome editing efficiency distribution for inactive gRNAs"\n+        "sd_act"\n+            arg_type = Float16\n+            help = "standard deviation of the genome editing efficiency distributions for active and inactive gRNAs"\n+        "n_gRNA_total"\n+            arg_type = Int\n+            help = "the total number of gRNAs in the experiment"\n+        "--visualize"\n+            action = :store_true\n+            # arg_type = Bool\n+            # default = false\n+            help = "if provided a histogram of all genome editing efficiency is plotted"\n+        "--out_file"\n+            arg_type = String\n+            default = "gRNA_edit"\n+            help = "Output excel file prefix"\n+    end\n+    \n+'..b'            end\n+                end\n+                # E_sim, sd_sim = BioCCP_\xce\xb3\xe2\x82\x93\xe2\x82\x82(x, N, g, r, t, f, e, E)\n+                out_dict["E_cov"] = exp_cov\n+                out_dict["N_95"] = N_95\n+                out_dict["pls"] = plant_library_sizes\n+\n+            elseif command_args["M"] == 9\n+                tool_info["mode"] = "BioCCP_\xce\xb3x3"\n+                tool_info["mode_description"] = "Computes the expected coverage of all "*\n+                    "triple combinations of gene knockouts (E[\xce\xb3x,3]) for an experiment with "*\n+                    "given plant library size using BioCCP"\n+\n+                if s != nothing && MN != nothing\n+                    plant_library_sizes = N:s:MN\n+                else\n+                    plant_library_sizes = N\n+                end\n+                exp_cov = []\n+                global  N_95 = nothing\n+                for N in plant_library_sizes  \n+                    E_cov = BioCCP_\xce\xb3\xe2\x82\x93\xe2\x82\x83(x, N, g, r, t, f, e, E)\n+                    push!(exp_cov, E_cov)\n+                    if E_cov < 0.95\n+                        N_95 = N\n+                    end\n+                end\n+                # E_sim, sd_sim = BioCCP_\xce\xb3\xe2\x82\x93\xe2\x82\x83(x, N, g, r, t, f, e, E)\n+                out_dict["E_cov"] = exp_cov\n+                out_dict["N_95"] = N_95\n+                out_dict["pls"] = plant_library_sizes\n+\n+            end\n+        end\n+    end\n+\n+    println(parsed_args)\n+    println("Parsed args:")\n+    for (key,val) in parsed_args\n+        println("  $key  =>  $(repr(val))")\n+    end\n+    println()\n+    println("Command: ", parsed_args["%COMMAND%"])\n+    # h1 = histogram(grna_dict["p_gRNA_reads"], label="", \n+    #                 xlabel="Number of reads per gRNA", \n+    #                 linecolor="white", \n+    #                 normalize=:probability,\n+    #                 xtickfontsize=10,ytickfontsize=10,\n+    #                 color=:mediumturquoise, size=(600,350), bins = 25,\n+    #                 ylabel="Relative frequency", \n+    #                 title="gRNA frequency distribution")\n+    \n+    # h2 = histogram(grna_dict["p_gRNA_edit"], \n+    #                 normalize = :probability,\n+    #                 linecolor = "white",\n+    #                 label="", \n+    #                 color=:turquoise4,\n+    #                 xtickfontsize=10,ytickfontsize=10, xlim = (0, 1),\n+    #                 xticks=(0:0.1:1),\n+    #                 bins = 150,\n+    #                 xlabel="gRNA editing efficiency", \n+    #                 ylabel="Relative frequency", \n+    #                 title="gRNA genome editing effiency distribution")\n+\n+    # p_plot = plot(plant_library_sizes, P\xe2\x82\x93\xe2\x82\x82, label="P\xe2\x82\x93\xe2\x82\x82", \n+    #             title="Probability of full combinatorial coverage with respect to plant library size",\n+    #             xlabel="N", ylabel="P\xe2\x82\x93\xe2\x82\x96", \n+    #             xticks = (0:500:50000, string.(0:500:50000)),\n+    #             size=(900,400), color=:turquoise4, linewidth=2)\n+    #             hline!([0.95], linestyle=:dash, color=:grey, label="P\xe2\x82\x93\xe2\x82\x96 = 0.95", legend=:bottomright)\n+\n+    # exp_plot = plot(plant_library_sizes, expected_\xce\xb3\xe2\x82\x93\xe2\x82\x82,\n+    #             label="E[\xce\xb3\xe2\x82\x93\xe2\x82\x82]", title="Expected combinatorial coverage w.r.t. plant library size",\n+    #             xlabel="N", ylabel="E[\xce\xb3\xe2\x82\x93\xe2\x82\x96]", \n+    #             xticks = (0:500:50000, string.(0:500:50000)),\n+    #             size=(800,400), color=:turquoise4, linewidth=2)\n+    #             hline!([0.95], linestyle=:dash, color=:grey, label="E[\xce\xb3\xe2\x82\x93\xe2\x82\x96] = 0.95", legend=:bottomright)\n+    \n+\n+    ENV["GKSwstype"]="nul"\n+    weave(string(@__DIR__) * "/report.jmd", \n+            args = (parsed_args = parsed_args,\n+                    tool_info = tool_info,\n+                    args_info = args_info,\n+                    grna_dict = grna_dict,\n+                    #h1 = h1, h2 = h2,\n+                    output = out_dict); \n+            doctype = "md2html", out_path = :pwd)\n+    ENV["GKSwstype"]="gksqt"\n+end\n+\n+main(ARGS)\n'
b
diff -r 000000000000 -r cc0957c46408 mcdoe.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcdoe.xml Thu May 12 17:39:18 2022 +0000
[
b'@@ -0,0 +1,341 @@\n+<tool id="mcdoe" name="MultiplexCrisprDOE - simulation- and BioCCP-based approaches for computing the minimal plant library size" version="0.1.0" python_template_version="3.5">\n+    <requirements>\n+        <requirement type="package" version="1.7.2">julia</requirement>\n+    </requirements>\n+    <command detect_errors="exit_code"><![CDATA[\n+        \n+        ## method $t $x $g $r $n_gRNA_total $filename $filename $\xcf\xb5_KO --i 10\n+\n+        #if $tools.tool_selector == "gfd"\n+            julia \'$__tool_directory__/main.jl\' gfd $tools.pls $tools.sd $tools.min_gRNA_abundance \n+            $tools.max_gRNA_abundance $tools.n_gRNA_total $tools.normalize\n+            &&\n+            mv ./gRNA_reads.xlsx $gRNA_reads\n+        #elif $tools.tool_selector == "ged"\n+            julia \'$__tool_directory__/main.jl\' ged $tools.f_act $tools.eps_edit_act $tools.eps_edit_inact \n+            $tools.sd_act $tools.n_gRNA_total\n+            &&\n+            mv ./gRNA_edit.xlsx $gRNA_edit\n+        #elif $tools.tool_selector == "sim"\n+            julia \'$__tool_directory__/main.jl\' sim $tools.simulation_mode.simulation_mode_selector $tools.n_target_genes \n+            $tools.n_designed_gRNAs $tools.simulation_mode.n_gRNA_seqs $tools.n_gRNA_total $tools.p_gRNA_freq \n+            $tools.p_gRNA_edit $tools.g_KO --iter $tools.iter\n+            #if $tools.simulation_mode.simulation_mode_selector == "4"\n+                &&\n+                mv ./countKOs.xlsx $countKOs\n+            #end if\n+        #elif $tools.tool_selector == "ccp"\n+            julia \'$__tool_directory__/main.jl\' ccp $tools.bioccp_mode.bioccp_mode_selector $tools.n_target_genes $tools.pls \n+            $tools.n_designed_gRNAs $tools.bioccp_mode.n_gRNA_seqs $tools.n_gRNA_total $tools.p_gRNA_freq \n+            $tools.p_gRNA_edit $tools.g_KO --step $tools.step --MN $tools.max_pls\n+        #end if\n+        &&\n+        mv ./report.html $report\n+\n+    ]]></command>\n+    <inputs>\n+        <conditional name="tools">\n+            <param name="tool_selector" type="select" label="Select tool for processing the alignment(s)">\n+                <option value="gfd">Generate vector with frequencies in the combinatorial gRNA/Cas9 construct library for all gRNAs</option>\n+                <option value="ged">Generate vector with genome editing efficiencies for all the gRNAs in the experiment</option>\n+                <option value="sim">Simulation-based approaches for computing the minimal plant library size that guarantees full combinatorial coverage</option>\n+                <option value="ccp">BioCCP-based approaches for computing the minimal plant library size that guarantees full combinatorial coverage</option>\n+            </param>\n+            <when value="gfd">\n+                <param name="pls" type="integer" value="75" optional="false" />\n+                <param name="sd" type="integer" value="25" optional="false" />\n+                <param name="min_gRNA_abundance" value="50" type="integer" optional="false" />\n+                <param name="max_gRNA_abundance" value="100" type="integer" optional="false" />\n+                <param name="n_gRNA_total" value="120" type="integer" optional="false" />\n+                <param name="normalize" type="boolean" truevalue="--normalize" falsevalue="" checked="False" label="Convert gRNA abundances into relative frequencies" />\n+            </when>\n+            <when value="ged">\n+                <param name="f_act" type="float" value="0.9" optional="false" />\n+                <param name="eps_edit_act" type="float" value="0.95" optional="false" />\n+                <param name="eps_edit_inact" type="float" value="0.1" optional="false" />\n+                <param name="sd_act" type="float" value="0.01" optional="false" />\n+                <param name="n_gRNA_total" type="integer" value="120" optional="false" />\n+            </when>\n+            <when value="sim">\n+                <conditional name="simulation_mode">\n+                    <param name="'..b'+                       efficiency distributions for active and\n+                       inactive gRNAs (type: Float16)\n+  n_gRNA_total         the total number of gRNAs in the experiment\n+                       (type: Int64)\n+\n+optional arguments:\n+  --visualize          if provided a histogram of all genome editing\n+                       efficiency is plotted\n+  --out_file OUT_FILE  Output excel file prefix (default: "gRNA_edit")\n+  -h, --help           show this help message and exit\n+\n+usage: main.jl sim [--i I] [-h] [M] [x] [g] [r] [t] [f] [e] [E]\n+\n+positional arguments:\n+  M              Select simulation mode (1: simulate_N\xe2\x82\x93\xe2\x82\x81; 2:\n+                 simulate_N\xe2\x82\x93\xe2\x82\x82; 3: simulate_N\xe2\x82\x93\xe2\x82\x83; 4:\n+                 simulate_N\xe2\x82\x93\xe2\x82\x82_countKOs) (type: Int64)\n+  x              number of target genes in the experiment (type:\n+                 Int64)\n+  g              number of gRNAs designed per target gene (type:\n+                 Int64)\n+  r              number of gRNA sequences per combinatorial gRNA/Cas\n+                 construct (type: Int64)\n+  t              total number of gRNAs in the experiment (type: Int64)\n+  f              vector with relative frequencies for all gRNAs in the\n+                 construct library (normalized!)\n+  e              vector with genome editing efficiencies for all gRNAs\n+  E              global knockout efficiency; fraction of mutations\n+                 leading to effective gene knockout (type: Float16)\n+\n+optional arguments:\n+  --i, --iter I  number of CRISPR/Cas experiments that are simulated\n+                 (type: Int64, default: 500)\n+  -h, --help     show this help message and exit\n+\n+usage: main.jl ccp [--s S] [--MN MN] [-h] [M] [x] [N] [g] [r] [t] [f]\n+                   [e] [E]\n+\n+positional arguments:\n+  M                     Select BioCCP mode (1: BioCCP_N\xe2\x82\x93\xe2\x82\x81; 2:\n+                        BioCCP_N\xe2\x82\x93\xe2\x82\x82; 3: BioCCP_N\xe2\x82\x93\xe2\x82\x83; 4: BioCCP_P\xe2\x82\x93\xe2\x82\x81; 5:\n+                        BioCCP_P\xe2\x82\x93\xe2\x82\x82 ; 6: BioCCP_P\xe2\x82\x93\xe2\x82\x83; 7: BioCCP_\xce\xb3\xe2\x82\x93\xe2\x82\x81; 8:\n+                        BioCCP_\xce\xb3\xe2\x82\x93\xe2\x82\x82; 9: BioCCP_\xce\xb3\xe2\x82\x93\xe2\x82\x83) (type: Int64)\n+  x                     number of target genes in the experiment\n+                        (type: Int64)\n+  N                     (Minimum) plant library size (type: Int64)\n+  g                     number of gRNAs designed per target gene\n+                        (type: Int64)\n+  r                     number of gRNA sequences per combinatorial\n+                        gRNA/Cas construct (type: Int64)\n+  t                     total number of gRNAs in the experiment (type:\n+                        Int64)\n+  f                     File containing vector with relative\n+                        frequencies for all gRNAs in the construct\n+                        library (normalized!)\n+  e                     File containing vector with genome editing\n+                        efficiencies for all gRNAs\n+  E                     global knockout efficiency; fraction of\n+                        mutations leading to effective gene knockout\n+                        (type: Float16)\n+\n+optional arguments:\n+  --s, --step S         Step size for plant library size (optional for\n+                        calculating expected combinatorial coverage /\n+                        plant library size) (type: Int64, default: 5)\n+  --MN, --max_pl_size MN\n+                        Maximum plant library size (optional for\n+                        calculating expected combinatorial coverage /\n+                        plant library size) (type: Int64, default:\n+                        4000)\n+  -h, --help            show this help message and exit\n+    ]]></help>\n+    <citations>\n+        <citation type="bibtex">\n+@misc{githubMultiplexCrisprDOE,\n+  author = {LastTODO, FirstTODO},\n+  year = {TODO},\n+  title = {MultiplexCrisprDOE},\n+  publisher = {GitHub},\n+  journal = {GitHub repository},\n+  url = {https://github.com/kirstvh/MultiplexCrisprDOE},\n+}</citation>\n+    </citations>\n+</tool>\n\\ No newline at end of file\n'
b
diff -r 000000000000 -r cc0957c46408 report.jmd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/report.jmd Thu May 12 17:39:18 2022 +0000
[
@@ -0,0 +1,102 @@
+---
+title: MultiplexCrisprDOE
+---
+
+<!-- this setup dependencies, but doesn't appear in the generated document -->
+```julia; echo = false; results = "hidden"
+using Pkg
+"Plots" ∉ keys(Pkg.project().dependencies) && Pkg.add("Plots")
+#"DSP" ∉ keys(Pkg.project().dependencies) && Pkg.add("DSP")
+#"Images" ∉ keys(Pkg.project().dependencies) && Pkg.add("Images")
+"DataStructures" ∉ keys(Pkg.project().dependencies) && Pkg.add("DataStructures")
+"PrettyTables" ∉ keys(Pkg.project().dependencies) && Pkg.add("PrettyTables")
+"DataFrames" ∉ keys(Pkg.project().dependencies) && Pkg.add("DataFrames")
+"Latexify" ∉ keys(Pkg.project().dependencies) && Pkg.add("Latexify")
+```
+## Tool
+
+* **Method:**     `j println(WEAVE_ARGS.tool_info["method"])`
+
+* **Description:** `j println(WEAVE_ARGS.tool_info["description"])`
+
+* **Mode:** `j println(WEAVE_ARGS.tool_info["mode"])`
+
+* **Mode description:** `j println(WEAVE_ARGS.tool_info["mode_description"])`
+
+## Variables
+
+```julia; echo = false
+using DataFrames
+using PrettyTables
+df = DataFrame("Argument" => collect(keys(WEAVE_ARGS.args_info)), "Value" => collect(values(WEAVE_ARGS.args_info)))
+#pt = pretty_table(df, nosubheader=true; alignment=:l)
+```
+
+```julia; echo = false
+    using Plots
+    if haskey(WEAVE_ARGS.grna_dict,"p_gRNA_reads")
+        h1 = histogram(WEAVE_ARGS.grna_dict["p_gRNA_reads"], label="", 
+                        xlabel="Number of reads per gRNA", 
+                        linecolor="white", 
+                        normalize=:probability,
+                        xtickfontsize=10,ytickfontsize=10,
+                        color=:mediumturquoise, size=(600,350), bins = 25,
+                        ylabel="Relative frequency", 
+                        title="gRNA frequency distribution")
+        display(h1)
+    end
+```
+
+```julia; echo = false
+    using Plots
+    if haskey(WEAVE_ARGS.grna_dict,"p_gRNA_edit")
+        h2 = histogram(WEAVE_ARGS.grna_dict["p_gRNA_edit"], 
+                        normalize = :probability,
+                        linecolor = "white",
+                        label="", 
+                        color=:turquoise4,
+                        xtickfontsize=10,ytickfontsize=10, xlim = (0, 1),
+                        xticks=(0:0.1:1),
+                        bins = 150,
+                        xlabel="gRNA editing efficiency", 
+                        ylabel="Relative frequency", 
+                        title="gRNA genome editing effiency distribution")
+        display(h2)
+    end
+```
+
+```julia; echo = false
+    using Plots
+    if haskey(WEAVE_ARGS.output,"output file")
+        println("Output written to:")
+        println(WEAVE_ARGS.output["output file"])
+    elseif haskey(WEAVE_ARGS.output,"E_sim")
+        E_sim = WEAVE_ARGS.output["E_sim"]
+        sd_sim = WEAVE_ARGS.output["sd_sim"]
+        k = WEAVE_ARGS.args_info["# of gRNAs / combi gRNA/Cas construct"]
+        x = WEAVE_ARGS.args_info["# of target genes in the experiment"]
+        println("**How many plants need to be included in the plant library (on average) to obtain full coverage of all k-combinations of gene knockouts?**")
+        println("On average, ", Int(ceil(E_sim)), " plants need to be sampled at random to observe all ", k, "-combinations of ", x, " gene knockouts. Standard deviation = ", Int(ceil(sd_sim)), " plants")
+    elseif haskey(WEAVE_ARGS.output,"P_sim")
+        p = plot(WEAVE_ARGS.output["pls"], WEAVE_ARGS.output["P_sim"], label="Pₓ₂", 
+                title="Probability of full combinatorial coverage with respect to plant library size",
+                xlabel="N", ylabel="Pₓₖ", 
+                xticks = (0:500:50000, string.(0:500:50000)),
+                size=(900,400), color=:turquoise4, linewidth=2)
+                hline!([0.95], linestyle=:dash, color=:grey, label="Pₓₖ = 0.95", legend=:bottomright)
+        display(p)
+        println("At a given number of plants, what is the probability that all pairwise combinations of gene knockouts are observed?")
+        println("N_95_P: ", WEAVE_ARGS.output["N_95_P"])
+    elseif haskey(WEAVE_ARGS.output,"E_cov")
+        p = plot(WEAVE_ARGS.output["pls"], WEAVE_ARGS.output["E_cov"],
+                label="E[γₓ₂]", title="Expected combinatorial coverage w.r.t. plant library size",
+                xlabel="N", ylabel="E[γₓₖ]", 
+                xticks = (0:500:50000, string.(0:500:50000)),
+                size=(800,400), color=:turquoise4, linewidth=2)
+                hline!([0.95], linestyle=:dash, color=:grey, label="E[γₓₖ] = 0.95", legend=:bottomright)
+        display(p)
+        println("At a given number of plants, what is the expected coverage of pairwise gene knockout combinations?")
+        println("N_95: ", WEAVE_ARGS.output["N_95"])
+    end
+```
+
b
diff -r 000000000000 -r cc0957c46408 test-data/example_data.xlsx
b
Binary file test-data/example_data.xlsx has changed
b
diff -r 000000000000 -r cc0957c46408 test-data/test_ccp_report.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/test_ccp_report.html Thu May 12 17:39:18 2022 +0000
[
b'@@ -0,0 +1,707 @@\n+<!DOCTYPE html>\n+<HTML lang = "en">\n+<HEAD>\n+  <meta charset="UTF-8"/>\n+  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">\n+  <title>MultiplexCrisprDOE</title>\n+  \n+\n+  <script type="text/x-mathjax-config">\n+    MathJax.Hub.Config({\n+      tex2jax: {inlineMath: [[\'$\',\'$\'], [\'\\\\(\',\'\\\\)\']]},\n+      TeX: { equationNumbers: { autoNumber: "AMS" } }\n+    });\n+  </script>\n+\n+  <script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML">\n+  </script>\n+\n+  \n+<style>\n+pre.hljl {\n+    border: 1px solid #ccc;\n+    margin: 5px;\n+    padding: 5px;\n+    overflow-x: auto;\n+    color: rgb(68,68,68); background-color: rgb(251,251,251); }\n+pre.hljl > span.hljl-t { }\n+pre.hljl > span.hljl-w { }\n+pre.hljl > span.hljl-e { }\n+pre.hljl > span.hljl-eB { }\n+pre.hljl > span.hljl-o { }\n+pre.hljl > span.hljl-k { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kc { color: rgb(59,151,46); font-style: italic; }\n+pre.hljl > span.hljl-kd { color: rgb(214,102,97); font-style: italic; }\n+pre.hljl > span.hljl-kn { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kp { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kr { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kt { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-n { }\n+pre.hljl > span.hljl-na { }\n+pre.hljl > span.hljl-nb { }\n+pre.hljl > span.hljl-nbp { }\n+pre.hljl > span.hljl-nc { }\n+pre.hljl > span.hljl-ncB { }\n+pre.hljl > span.hljl-nd { color: rgb(214,102,97); }\n+pre.hljl > span.hljl-ne { }\n+pre.hljl > span.hljl-neB { }\n+pre.hljl > span.hljl-nf { color: rgb(66,102,213); }\n+pre.hljl > span.hljl-nfm { color: rgb(66,102,213); }\n+pre.hljl > span.hljl-np { }\n+pre.hljl > span.hljl-nl { }\n+pre.hljl > span.hljl-nn { }\n+pre.hljl > span.hljl-no { }\n+pre.hljl > span.hljl-nt { }\n+pre.hljl > span.hljl-nv { }\n+pre.hljl > span.hljl-nvc { }\n+pre.hljl > span.hljl-nvg { }\n+pre.hljl > span.hljl-nvi { }\n+pre.hljl > span.hljl-nvm { }\n+pre.hljl > span.hljl-l { }\n+pre.hljl > span.hljl-ld { color: rgb(148,91,176); font-style: italic; }\n+pre.hljl > span.hljl-s { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sa { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sb { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sc { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sd { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sdB { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sdC { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-se { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-sh { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-si { }\n+pre.hljl > span.hljl-so { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sr { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-ss { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-ssB { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-nB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nbB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nfB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nh { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-ni { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nil { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-noB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-oB { color: rgb(102,102,102); font-weight: bold; }\n+pre.hljl > span.hljl-ow { color: rgb(102,102,102); font-weight: bold; }\n+pre.hljl > span.hljl-p { }\n+pre.hljl > span.hljl-c { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-ch { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cm { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cp { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cpB { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cs { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-csB { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-g { }\n+pre.'..b'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"  />\n+\n+\n+        <HR/>\n+        <div class="footer">\n+          <p>\n+            Published from <a href="report.jmd">report.jmd</a>\n+            using <a href="http://github.com/JunoLab/Weave.jl">Weave.jl</a> v0.10.10 on 2022-05-12.\n+          </p>\n+        </div>\n+      </div>\n+    </div>\n+  </div>\n+</BODY>\n+\n+</HTML>\n'
b
diff -r 000000000000 -r cc0957c46408 test-data/test_countKOs.xlsx
b
Binary file test-data/test_countKOs.xlsx has changed
b
diff -r 000000000000 -r cc0957c46408 test-data/test_gRNA_edit.xlsx
b
Binary file test-data/test_gRNA_edit.xlsx has changed
b
diff -r 000000000000 -r cc0957c46408 test-data/test_gRNA_reads.xlsx
b
Binary file test-data/test_gRNA_reads.xlsx has changed
b
diff -r 000000000000 -r cc0957c46408 test-data/test_ged_report.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/test_ged_report.html Thu May 12 17:39:18 2022 +0000
[
b'@@ -0,0 +1,704 @@\n+<!DOCTYPE html>\n+<HTML lang = "en">\n+<HEAD>\n+  <meta charset="UTF-8"/>\n+  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">\n+  <title>MultiplexCrisprDOE</title>\n+  \n+\n+  <script type="text/x-mathjax-config">\n+    MathJax.Hub.Config({\n+      tex2jax: {inlineMath: [[\'$\',\'$\'], [\'\\\\(\',\'\\\\)\']]},\n+      TeX: { equationNumbers: { autoNumber: "AMS" } }\n+    });\n+  </script>\n+\n+  <script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML">\n+  </script>\n+\n+  \n+<style>\n+pre.hljl {\n+    border: 1px solid #ccc;\n+    margin: 5px;\n+    padding: 5px;\n+    overflow-x: auto;\n+    color: rgb(68,68,68); background-color: rgb(251,251,251); }\n+pre.hljl > span.hljl-t { }\n+pre.hljl > span.hljl-w { }\n+pre.hljl > span.hljl-e { }\n+pre.hljl > span.hljl-eB { }\n+pre.hljl > span.hljl-o { }\n+pre.hljl > span.hljl-k { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kc { color: rgb(59,151,46); font-style: italic; }\n+pre.hljl > span.hljl-kd { color: rgb(214,102,97); font-style: italic; }\n+pre.hljl > span.hljl-kn { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kp { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kr { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kt { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-n { }\n+pre.hljl > span.hljl-na { }\n+pre.hljl > span.hljl-nb { }\n+pre.hljl > span.hljl-nbp { }\n+pre.hljl > span.hljl-nc { }\n+pre.hljl > span.hljl-ncB { }\n+pre.hljl > span.hljl-nd { color: rgb(214,102,97); }\n+pre.hljl > span.hljl-ne { }\n+pre.hljl > span.hljl-neB { }\n+pre.hljl > span.hljl-nf { color: rgb(66,102,213); }\n+pre.hljl > span.hljl-nfm { color: rgb(66,102,213); }\n+pre.hljl > span.hljl-np { }\n+pre.hljl > span.hljl-nl { }\n+pre.hljl > span.hljl-nn { }\n+pre.hljl > span.hljl-no { }\n+pre.hljl > span.hljl-nt { }\n+pre.hljl > span.hljl-nv { }\n+pre.hljl > span.hljl-nvc { }\n+pre.hljl > span.hljl-nvg { }\n+pre.hljl > span.hljl-nvi { }\n+pre.hljl > span.hljl-nvm { }\n+pre.hljl > span.hljl-l { }\n+pre.hljl > span.hljl-ld { color: rgb(148,91,176); font-style: italic; }\n+pre.hljl > span.hljl-s { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sa { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sb { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sc { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sd { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sdB { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sdC { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-se { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-sh { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-si { }\n+pre.hljl > span.hljl-so { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sr { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-ss { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-ssB { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-nB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nbB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nfB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nh { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-ni { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nil { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-noB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-oB { color: rgb(102,102,102); font-weight: bold; }\n+pre.hljl > span.hljl-ow { color: rgb(102,102,102); font-weight: bold; }\n+pre.hljl > span.hljl-p { }\n+pre.hljl > span.hljl-c { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-ch { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cm { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cp { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cpB { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cs { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-csB { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-g { }\n+pre.'..b'yHh0dTUxOqvXz5MnrpOzg4JCcnAwDOnz8Pa/38/ObOnUsQxA8//KDy6MFIqe7u7kFBQbAxnEvMzc1FOtLGxoYcEPb69esoDbyFhUV2djYAIDk5eTTJ8/PzkWwMBmPdunUwYuzWrVvJkvj7+6vkIoHBcM3MzCIiItDRCgsLyV8VEydOhBGHYd5epVIJm8lkstDQUKR3bW1tL1y4QP7Ltm3b9vbbb0OtxmQyUWR0SFJSkrm5OToLTEZx7NgxOp2uEq17/vz5rq6uWv44zH8erMAwukJlZSVM5tve3i4Wi3ft2gWV2WgK7I8//gAArFmzBpVABTY0NLRy5UoOhwPzGKgrMKlUamhouH37doIgzp07BwAoLy8fTSqFQuHs7Ozo6FhaWiqTya5cueLm5ubo6AjDn3d3d1tYWEyePLmmpqavry8hIYHD4WhUYHK5HBprZGZmtrS0tLS0KBQKQpMCs7GxyczMvHPnTllZmZWVFUr4olQqYYLNS5cuyeXy7777DoZjH02BVVZWMpnM4ODg5ubm3t7es2fP6uvrw6sWi8XFxcUAgFOnTrW0tHR0dLS3t3/44YcAgNra2paWlu7ubuKvCqyyspLFYs2aNau6ulomkzU2Nh44cECjAgsICOByuTk5Ob29vc3Nza+88oqxsbFIJIK1HA7HyMho9erVTU1Nt27dCgoKYjKZ6IMAGrwsX768vr5eJpNdvXr18OHDBEH09PQYGBjs2bMHXV1LSwudTv/8889H++MwzwJYgWF0hZCQEB6PJ5fLUQlM10lWYC+88AJMMrlly5bx48fz+fzm5mbUHikwmG8JJlhSV2DQxA7mQhsYGODxeKtWrRpNqqSkJDiqQyUwh2pBQQFBECdPngQAVFdXo9q1a9dqVGAEQVRVVQEASkpKyMdXV2BHjx5Ftfv372cwGFBZfvvttwCAwsJCVAsTk46mwPz9/V1cXAYHB1FJVFSUkZERHG7C5JPnzp1DtcePHyf3NvFXBbZw4UIej6cx0w1Zgf3666/gr7nTenp6jIyM0DCLw+FMmTIFpfOAyUW//PJLuGtvb+/u7o5qyaxZs8bKygqlTYmMjNTX14eKFvPMgtfAMLoCTF9LXriaP3/+pUuXyG1u376dkJAwNDR0584dGo2Wn5+vcanMzs5u7dq1R48e3bBhg3ptSkrKtGnT4KQZi8Vavnx5cnLysWPHNBpTlJWVGRkZ3bhx48aNG7CEIAgmk1lfX79w4UKhUGhubg4zFUEWLlwIFeRj4+Pjg7YdHByGh4c7OzsnTJhw/fp1Op0+b948VPvyyy+PdhCCIMrKynx8fOAQEzIwMCCVSkUikY2NzaNKVVFRsWTJkoem2Lhw4QIA4MGDBzk5OaiQx+PV19ejXW9vb7QqZmlpaWBgABPGt7e3t7a2xsbGalwz27hxY1JSUkFBQUBAwODgYFpaWnBwMBzvYp5ZsALD6ApdXV3I/g2inidp5syZ58+fBwCIxeLAwMBly5bV1dVZWVmpH23v3r1paWmxsbE7d+4klzc2NlZWVs6dOzc+Ph6WyGQyuVyek5MTFhamUaqhoSHUGOLm5gYNKO7evTt+/HhyFXn95vEg61EmkwkAgBbqEomEzWaTDTdgvlCNKBQKpVIpFApVJPfw8JDJZI8q0tDQkEwmG4s9hUQiAQCoqHBTU1OUsxQAoKIFWSwWvMDe3l4AwGhnmTp16owZMxISEgICAr799tvOzs5169Y96oVg/mNgBYbRFWxtbdvb28klyB1YnfHjx585c8bR0XHbtm1fffWVegMej7d58+b4+HhktQhJSUlhMBi3bt1KSEhAhQYGBsnJyRoVGJfLHTdu3G+//aZuKwgAsLa2LikpIZfAOTEqsLW1lcvlEokE6S0t/aOvr89msxcsWHD69Om/f2omk2lqaioSiR7aEho65uTk2NvbP+pZ4KeAlrOsX78+LCzs1q1biYmJbm5u06dPf9RTYP5jYEdmjK7g5eVVUVHR1NQEd2UyGbSyGw1ra+uNGzdmZ2fX1NRobBAeHm5sbBwdHY1KhoaGMjIy/Pz8Wv7K3r17L1682NDQoH4QPz+/3t5eFY9phLu7u0QigfNmEC1hNeB812MMgCBeXl4AALJC0qKcaDSar6/v999/39XV9XinU8HX1zc/P//evXvam0GvgNTU1Mc4haWlpaur65kzZwYHBzU2CAoK4nA4UVFRP//8s3Y/P8wzAlZgGF3hgw8+MDc3nzNnzieffHLixAlvb28ejwcA0Dj0gURERBgaGkJre3VMTEx27NhRUVGBSoqKijo6Ot566y2VlsHBwXQ6PT09Xf0gy5cv9/b2DgsLO3To0O+///7HH3/k5OQsW7bs+vXrAICgoCAHB4eVK1d+9913QqFwz549cIZTI7a2tlwu95NPPsnIyMjJybl7967W/lDlpZdeCg4O3rlz5+bNm0+fPr1ixYorV66A0fsnNjZWqVTOmzcvNze3oaHh4sWLx48fDwoKeqSTIj766CMAwJw5c86dO1dfX19SUrJhwwb18FGurq5r166NjY0NDw+/dOlSXV3dDz/88O67744xXNbHH3988+ZNX1/f4uLi+vr6goKCHTt2oFo2mx0aGgrNKVesWPF4F4L5L4EVGEZX4PF4v/zyy+zZsxMTEzMyMt57773g4GADAwPkGWZjY6Oy3DV+/Pjw8HBo+Q0AMDMzEwgE5AabNm3y8PAQCATIut3JySkgIEDl1DY2NkuXLq2oqCAIQqWKyWQWFRWtWrXqwIEDHh4eU6dOXbduHYvFgsqVzWYXFRVNmDAhICBgypQp5eXlqampAoGAzWaji4ItYeOcnBw2mx0TExMZGQnHmhYWFsi/2NDQUCAQkENaqJSkpKTs3r27tLT00KFDZmZmcAQ22qLRpEmTLl68aGVlFRwc7Ozs7O3tffz4ceTvNW7cOIFAgPoWAGBiYiIQCMgGFHw+H1lJODk5lZWV8Xi8ZcuWubi4+Pv737x5E+pOIyMja2trpEdPnjx54MCB7OxsLy+vSZMmBQUFiUQi5NlmZ2ensnRnZ2eHFjv9/f3z8/N7enqgCWVgYGBfXx+58erVqwEAwcHBDzUnwTwL0NSfWAxGR3jttdf+/PPP2traf1sQAAADEZY8AAABSElEQVQYGRm5f/8+m83W+Ors6upiMplkU4UnQFpaWmhoaENDA/Ju1ohSqezu7jYzM9PT0/v7J5XL5X19febm5iohQtSBfngqRi5jpK+vT6FQWFhYqIwvs7KygoODq6uryZafmGcWrMAwOkRaWtq8efNsbGykUumpU6d27NgRFxe3ffv2f1suXaGkpMTIyMjd3R1ayb/zzjvW1tbQ9epZoKenx9vb29zcXMVwBvPMgq0QMTpEQkJCaGgom81+8OABk8lcv379tm3b/m2hdIirV69GREQwGAwajTY4ODhjxgyNFpj/STw9Paurq42NjbOysv5tWTC6Ah6BYXSLpqamzs5OGo02adIkLX5Ozyxisbi5uVkul9vZ2Wl04v6vUlVVNTg46Obm9oTnaTG6DFZgGAwGg3kqwVaIGAwGg3kqwQoMg8FgME8l/weAUSxWp2R2fwAAAABJRU5ErkJggg=="  />\n+\n+<pre class="output">\n+Output written to:\n+gRNA_edit.xlsx\n+</pre>\n+\n+\n+\n+        <HR/>\n+        <div class="footer">\n+          <p>\n+            Published from <a href="report.jmd">report.jmd</a>\n+            using <a href="http://github.com/JunoLab/Weave.jl">Weave.jl</a> v0.10.10 on 2022-05-12.\n+          </p>\n+        </div>\n+      </div>\n+    </div>\n+  </div>\n+</BODY>\n+\n+</HTML>\n'
b
diff -r 000000000000 -r cc0957c46408 test-data/test_gfd_report.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/test_gfd_report.html Thu May 12 17:39:18 2022 +0000
[
b'@@ -0,0 +1,704 @@\n+<!DOCTYPE html>\n+<HTML lang = "en">\n+<HEAD>\n+  <meta charset="UTF-8"/>\n+  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">\n+  <title>MultiplexCrisprDOE</title>\n+  \n+\n+  <script type="text/x-mathjax-config">\n+    MathJax.Hub.Config({\n+      tex2jax: {inlineMath: [[\'$\',\'$\'], [\'\\\\(\',\'\\\\)\']]},\n+      TeX: { equationNumbers: { autoNumber: "AMS" } }\n+    });\n+  </script>\n+\n+  <script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML">\n+  </script>\n+\n+  \n+<style>\n+pre.hljl {\n+    border: 1px solid #ccc;\n+    margin: 5px;\n+    padding: 5px;\n+    overflow-x: auto;\n+    color: rgb(68,68,68); background-color: rgb(251,251,251); }\n+pre.hljl > span.hljl-t { }\n+pre.hljl > span.hljl-w { }\n+pre.hljl > span.hljl-e { }\n+pre.hljl > span.hljl-eB { }\n+pre.hljl > span.hljl-o { }\n+pre.hljl > span.hljl-k { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kc { color: rgb(59,151,46); font-style: italic; }\n+pre.hljl > span.hljl-kd { color: rgb(214,102,97); font-style: italic; }\n+pre.hljl > span.hljl-kn { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kp { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kr { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kt { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-n { }\n+pre.hljl > span.hljl-na { }\n+pre.hljl > span.hljl-nb { }\n+pre.hljl > span.hljl-nbp { }\n+pre.hljl > span.hljl-nc { }\n+pre.hljl > span.hljl-ncB { }\n+pre.hljl > span.hljl-nd { color: rgb(214,102,97); }\n+pre.hljl > span.hljl-ne { }\n+pre.hljl > span.hljl-neB { }\n+pre.hljl > span.hljl-nf { color: rgb(66,102,213); }\n+pre.hljl > span.hljl-nfm { color: rgb(66,102,213); }\n+pre.hljl > span.hljl-np { }\n+pre.hljl > span.hljl-nl { }\n+pre.hljl > span.hljl-nn { }\n+pre.hljl > span.hljl-no { }\n+pre.hljl > span.hljl-nt { }\n+pre.hljl > span.hljl-nv { }\n+pre.hljl > span.hljl-nvc { }\n+pre.hljl > span.hljl-nvg { }\n+pre.hljl > span.hljl-nvi { }\n+pre.hljl > span.hljl-nvm { }\n+pre.hljl > span.hljl-l { }\n+pre.hljl > span.hljl-ld { color: rgb(148,91,176); font-style: italic; }\n+pre.hljl > span.hljl-s { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sa { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sb { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sc { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sd { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sdB { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sdC { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-se { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-sh { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-si { }\n+pre.hljl > span.hljl-so { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sr { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-ss { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-ssB { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-nB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nbB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nfB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nh { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-ni { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nil { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-noB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-oB { color: rgb(102,102,102); font-weight: bold; }\n+pre.hljl > span.hljl-ow { color: rgb(102,102,102); font-weight: bold; }\n+pre.hljl > span.hljl-p { }\n+pre.hljl > span.hljl-c { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-ch { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cm { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cp { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cpB { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cs { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-csB { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-g { }\n+pre.'..b'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g=="  />\n+\n+\n+<pre class="output">\n+Output written to:\n+gRNA_reads.xlsx\n+</pre>\n+\n+\n+\n+        <HR/>\n+        <div class="footer">\n+          <p>\n+            Published from <a href="report.jmd">report.jmd</a>\n+            using <a href="http://github.com/JunoLab/Weave.jl">Weave.jl</a> v0.10.10 on 2022-05-11.\n+          </p>\n+        </div>\n+      </div>\n+    </div>\n+  </div>\n+</BODY>\n+\n+</HTML>\n'
b
diff -r 000000000000 -r cc0957c46408 test-data/test_sim_report.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/test_sim_report.html Thu May 12 17:39:18 2022 +0000
[
b'@@ -0,0 +1,705 @@\n+<!DOCTYPE html>\n+<HTML lang = "en">\n+<HEAD>\n+  <meta charset="UTF-8"/>\n+  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">\n+  <title>MultiplexCrisprDOE</title>\n+  \n+\n+  <script type="text/x-mathjax-config">\n+    MathJax.Hub.Config({\n+      tex2jax: {inlineMath: [[\'$\',\'$\'], [\'\\\\(\',\'\\\\)\']]},\n+      TeX: { equationNumbers: { autoNumber: "AMS" } }\n+    });\n+  </script>\n+\n+  <script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML">\n+  </script>\n+\n+  \n+<style>\n+pre.hljl {\n+    border: 1px solid #ccc;\n+    margin: 5px;\n+    padding: 5px;\n+    overflow-x: auto;\n+    color: rgb(68,68,68); background-color: rgb(251,251,251); }\n+pre.hljl > span.hljl-t { }\n+pre.hljl > span.hljl-w { }\n+pre.hljl > span.hljl-e { }\n+pre.hljl > span.hljl-eB { }\n+pre.hljl > span.hljl-o { }\n+pre.hljl > span.hljl-k { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kc { color: rgb(59,151,46); font-style: italic; }\n+pre.hljl > span.hljl-kd { color: rgb(214,102,97); font-style: italic; }\n+pre.hljl > span.hljl-kn { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kp { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kr { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-kt { color: rgb(148,91,176); font-weight: bold; }\n+pre.hljl > span.hljl-n { }\n+pre.hljl > span.hljl-na { }\n+pre.hljl > span.hljl-nb { }\n+pre.hljl > span.hljl-nbp { }\n+pre.hljl > span.hljl-nc { }\n+pre.hljl > span.hljl-ncB { }\n+pre.hljl > span.hljl-nd { color: rgb(214,102,97); }\n+pre.hljl > span.hljl-ne { }\n+pre.hljl > span.hljl-neB { }\n+pre.hljl > span.hljl-nf { color: rgb(66,102,213); }\n+pre.hljl > span.hljl-nfm { color: rgb(66,102,213); }\n+pre.hljl > span.hljl-np { }\n+pre.hljl > span.hljl-nl { }\n+pre.hljl > span.hljl-nn { }\n+pre.hljl > span.hljl-no { }\n+pre.hljl > span.hljl-nt { }\n+pre.hljl > span.hljl-nv { }\n+pre.hljl > span.hljl-nvc { }\n+pre.hljl > span.hljl-nvg { }\n+pre.hljl > span.hljl-nvi { }\n+pre.hljl > span.hljl-nvm { }\n+pre.hljl > span.hljl-l { }\n+pre.hljl > span.hljl-ld { color: rgb(148,91,176); font-style: italic; }\n+pre.hljl > span.hljl-s { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sa { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sb { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sc { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sd { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sdB { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sdC { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-se { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-sh { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-si { }\n+pre.hljl > span.hljl-so { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-sr { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-ss { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-ssB { color: rgb(201,61,57); }\n+pre.hljl > span.hljl-nB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nbB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nfB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nh { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-ni { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-nil { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-noB { color: rgb(59,151,46); }\n+pre.hljl > span.hljl-oB { color: rgb(102,102,102); font-weight: bold; }\n+pre.hljl > span.hljl-ow { color: rgb(102,102,102); font-weight: bold; }\n+pre.hljl > span.hljl-p { }\n+pre.hljl > span.hljl-c { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-ch { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cm { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cp { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cpB { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-cs { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-csB { color: rgb(153,153,119); font-style: italic; }\n+pre.hljl > span.hljl-g { }\n+pre.'..b'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"  />\n+\n+<pre class="output">\n+Output written to:\n+countKOs.xlsx\n+</pre>\n+\n+\n+\n+        <HR/>\n+        <div class="footer">\n+          <p>\n+            Published from <a href="report.jmd">report.jmd</a>\n+            using <a href="http://github.com/JunoLab/Weave.jl">Weave.jl</a> v0.10.10 on 2022-05-12.\n+          </p>\n+        </div>\n+      </div>\n+    </div>\n+  </div>\n+</BODY>\n+\n+</HTML>\n'