Repository: chrovis/cljam
Pull request #169 (2019-07-01 05:10:34)

Ignore undeclared keys in INFO and FORMAT when writing BCF files
`cljam.io.bcf.writer/write-variants` fails to encode variants if they contain keys not declared in `meta-info`.
This PR fixes the bug by ignoring such keys.

https://github.com/chrovis/cljam/blob/94ce1f8de25130bc2a28aa5a03e3fec3e34672f9/test/cljam/io/bcf/writer_test.clj#L256-L258
https://travis-ci.org/chrovis/cljam/jobs/552587080#L535-L554


Patch:
diff --git a/src/cljam/io/bcf/writer.clj b/src/cljam/io/bcf/writer.clj
index e6f3794..7fd993f 100644
--- a/src/cljam/io/bcf/writer.clj
+++ b/src/cljam/io/bcf/writer.clj
@@ -236,7 +236,7 @@
 (defn- parsed-variant->bcf-map
   "Converts a parsed variant map to BCF-style map."
   [[fmt-kw & indiv-kws :as kws] contigs filters formats info variant]
-  (let [fmts (map (juxt identity formats) (variant fmt-kw))
+  (let [fmts (keep (fn [f] (when-let [m (formats f)] [f m])) (variant fmt-kw))
         genotype (map
                   (fn [[k {:keys [idx type-kw]}]]
                     (->> indiv-kws
@@ -254,9 +254,9 @@
         (update :chr (comp :idx contigs))
         (update :filter (fn [f] (map (comp :idx filters) f)))
         (update :info (fn [i]
-                        (map (fn [[k v]]
-                               (let [{:keys [idx type-kw]} (info k)]
-                                 [idx type-kw v])) i))))))
+                        (keep (fn [[k v]]
+                                (when-let [{:keys [idx type-kw]} (info k)]
+                                  [idx type-kw v])) i))))))
 
 (defn- meta->map
   "Creates a map for searching meta-info with (f id)."


Test patch:
diff --git a/test/cljam/io/bcf/writer_test.clj b/test/cljam/io/bcf/writer_test.clj
index 83639be..07a229c 100644
--- a/test/cljam/io/bcf/writer_test.clj
+++ b/test/cljam/io/bcf/writer_test.clj
@@ -1,8 +1,10 @@
 (ns cljam.io.bcf.writer-test
   (:require [clojure.test :refer :all]
             [clojure.string :as cstr]
-            [cljam.io.bcf.writer :as bcf-writer])
-  (:import [java.nio ByteBuffer]))
+            [cljam.io.bcf.writer :as bcf-writer]
+            [cljam.io.util.bgzf :as bgzf])
+  (:import [java.nio ByteBuffer ByteOrder]
+           [java.io ByteArrayOutputStream File]))
 
 (deftest about-encode-typed-value
   (let [ba (#'bcf-writer/encode-typed-value :int (vec (repeat 14 1)))]
@@ -210,3 +212,58 @@
      0x80 0x81
      0x02 0x02
      0x02 0x04]))
+
+(defn- bgzf->bb ^ByteBuffer [f]
+  (with-open [is (bgzf/bgzf-input-stream f)
+              baos (ByteArrayOutputStream.)]
+    (while (pos? (.available is))
+      (.write baos (.read is)))
+    (.flush baos)
+    (doto (ByteBuffer/wrap (.toByteArray baos))
+      (.order ByteOrder/LITTLE_ENDIAN))))
+
+(defn- read-variant-blocks! [f]
+  (let [bb (bgzf->bb f)]
+    (doseq [b (map byte [\B \C \F 2 2])]
+      (assert (= b (.get bb))))
+    (.position bb (+ (.getInt bb) (.position bb)))
+    (->> (repeatedly
+          #(when (.hasRemaining bb)
+             (let [shared (byte-array (.getInt bb))
+                   indiv (byte-array (.getInt bb))]
+               (.get bb shared)
+               (.get bb indiv)
+               [(vec shared) (vec indiv)])))
+         (take-while some?)
+         vec)))
+
+(defn- write-variants&read-blocks [meta-info header variants]
+  (let [tmp (File/createTempFile "write-variants" ".bcf")]
+    (with-open [w (bcf-writer/writer tmp meta-info header)]
+      (bcf-writer/write-variants w variants))
+    (let [blocks (read-variant-blocks! tmp)]
+      (.delete tmp)
+      blocks)))
+
+(deftest write-variants
+  (let [meta-info {:fileformat "VCFv4.3"
+                   :contig [{:id "1", :length 100}]}
+        header ["CHROM" "POS" "ID" "REF" "ALT" "QUAL" "FILTER" "INFO"
+                "FORMAT" "SAMPLE"]]
+    (are [?variants ?blocks]
+         (= (map #(map (partial map unchecked-byte) %) ?blocks)
+            (write-variants&read-blocks meta-info header ?variants))
+      [{:chr "1", :pos 10, :ref "A", :alt ["C"],
+        :info {:UNDECLARED-INFO #{"???"}},
+        :FORMAT [:UNDECLARED-FORMAT]}]
+      [[[0x00 0x00 0x00 0x00
+         0x09 0x00 0x00 0x00
+         0x01 0x00 0x00 0x00
+         0x01 0x00 0x80 0x7f
+         0x00 0x00 0x02 0x00
+         0x01 0x00 0x00 0x00
+         0x07
+         0x17 0x41
+         0x17 0x43
+         0x00]
+        []]])))