defmodule Bio.Rebase.Emboss do @moduledoc false @keep_elements [:blunt?, :cut_1, :cut_2, :cut_3, :cut_4, :name, :pattern] def parse(patterns_file, information_file, suppliers_file) do [ parse_patterns(patterns_file), parse_information(information_file), parse_suppliers(suppliers_file) ] |> convolve() end defp convolve(data) do [patterns, info, _suppliers] = data # Patterns and info should have the same len, suppliers is just a map that # we can use to replace the weird keys with actual values patterns |> Enum.map(fn pat_map -> info_map = Enum.find(info, fn inf_el -> Map.get(inf_el, :enzyme_name) == Map.get(pat_map, :name) end) new = info_map |> Enum.reduce(%{}, fn {key, value}, inner -> Map.put(inner, key, value) end) pat_map |> Enum.reduce(new, fn {key, value}, inner -> Map.put(inner, key, value) end) end) |> Enum.map(fn full_map -> cond do # This data is confusing, and possibly wrong. full_map.name in ["HpyUM037X"] -> nil true -> full_map |> Enum.reduce(%{}, fn {key, value}, acc -> cond do key in @keep_elements -> Map.put(acc, key, value) true -> acc end end) end end) |> Enum.filter(&Function.identity/1) end # Format: # Line 1: Name of Enzyme # Line 2: Organism # Line 3: Isoschizomers # Line 4: Methylation # Line 5: Source # Line 6: Suppliers # Line 7: Number of following references # Lines 8..n: References # // (end of entry marker) defp parse_information(file) do File.read!(file) |> split_newline() |> Enum.filter(¬_comment?/1) |> Enum.join("\n") |> String.split("//") |> Enum.map(&String.trim/1) |> Enum.map(&split_newline/1) |> Enum.filter(¬_empty?/1) |> Enum.map(&references_collection/1) end defp references_collection(block) do Enum.with_index(block, 1) |> Enum.reduce(%{}, fn {line, idx}, acc -> case idx do 1 -> Map.put(acc, :enzyme_name, line) 2 -> Map.put(acc, :organism_name, line) 3 -> Map.put(acc, :isoschizomers, line) 4 -> Map.put(acc, :methylation, line) 5 -> Map.put(acc, :source, line) 6 -> Map.put(acc, :suppliers, String.graphemes(line)) 7 -> Map.put(acc, :number_of_references, line) _ -> Map.put(acc, :references, List.insert_at(Map.get(acc, :references, []), -1, line)) end end) end # Format: # namepatternlenncutsbluntc1c2c3c4 # # Where: # name = name of enzyme # pattern = recognition site # len = length of pattern # ncuts = number of cuts made by enzyme # Zero represents unknown # blunt = true if blunt end cut, false if sticky # c1 = First 5' cut # c2 = First 3' cut # c3 = Second 5' cut # c4 = Second 3' cut defp parse_patterns(file) do File.read!(file) |> split_newline() |> Enum.filter(¬_comment?/1) |> Enum.filter(¬_empty?/1) |> Enum.map(&information_collection/1) end defp information_collection(line) do line |> String.split("\t") |> Enum.with_index(1) |> Enum.reduce(%{}, fn {el, idx}, acc -> case idx do 1 -> Map.put(acc, :name, el) 2 -> Map.put(acc, :pattern, String.downcase(el)) 3 -> Map.put(acc, :length, String.to_integer(el)) 4 -> Map.put(acc, :number_cuts, String.to_integer(el)) 5 -> Map.put(acc, :blunt?, cast(el)) 6 -> Map.put(acc, :cut_1, String.to_integer(el)) 7 -> Map.put(acc, :cut_2, String.to_integer(el)) 8 -> Map.put(acc, :cut_3, String.to_integer(el)) 9 -> Map.put(acc, :cut_4, String.to_integer(el)) end end) end defp cast(str) do cond do str == "0" -> false str == "false" -> false str == "False" -> false str == "no" -> false str == "No" -> false str == "1" -> true str == "true" -> true str == "True" -> true str == "yes" -> true str == "Yes" -> true end end # Format: # Code of SupplierSupplier name defp parse_suppliers(file) do File.read!(file) |> split_newline() |> Enum.filter(¬_comment?/1) |> Enum.filter(¬_empty?/1) |> Enum.map(fn str -> String.split_at(str, 1) end) |> Enum.reduce(%{}, fn {code, supplier}, acc -> Map.put(acc, code, String.trim(supplier)) end) end defp not_comment?(str) do !String.starts_with?(str, "#") end defp split_newline(block) do String.split(block, "\n") end defp not_empty?(thing) when is_binary(thing) do cond do thing == "" -> false true -> true end end defp not_empty?(thing) when is_list(thing) do cond do thing == [] -> false Enum.all?(thing, fn el -> el == "" end) -> false true -> true end end end