{"action":"created","comment":{"url":"https://api.github.com/repos/ga4gh/server/pulls/comments/25678837","id":25678837,"diff_hunk":"@@ -6,35 +6,236 @@\n from __future__ import print_function\n from __future__ import unicode_literals\n \n+import collections\n+\n+import pysam\n+\n+import ga4gh.protocol as protocol\n+\n \n class AbstractConverter(object):\n     \"\"\"\n     Abstract base class for converter classes\n     \"\"\"\n-    def __init__(self, request, outputStream):\n-        self._request = request\n-        self._outputStream = outputStream\n+    def __init__(self, httpClient):\n+        self._httpClient = httpClient\n+\n+\n+##############################################################################\n+# SAM\n+##############################################################################\n+\n+\n+class SamException(Exception):\n+    \"\"\"\n+    Something that went wrong during converting a SAM file\n+    \"\"\"\n \n \n class SamConverter(AbstractConverter):\n     \"\"\"\n     Converts a request to a SAM file\n     \"\"\"\n-    def __init__(self, searchReadsRequest, outputStream):\n-        super(SamConverter, self).__init__(\n-            searchReadsRequest, outputStream)\n+    def __init__(self, httpClient, searchReadsRequest, outputFile,\n+                 binaryOutput):\n+        super(SamConverter, self).__init__(httpClient)\n+        self._searchReadsRequest = searchReadsRequest\n+        self._outputFile = outputFile\n+        self._binaryOutput = binaryOutput\n \n     def convert(self):\n-        raise NotImplementedError()\n+        header = self._getHeader()\n+        targetIds = self._getTargetIds(header)\n+        # pysam can't write to file streams (except for stdout)\n+        # http://pysam.readthedocs.org/en/latest/usage.html#using-streams\n+        if self._binaryOutput:\n+            flags = \"wb\"\n+        else:\n+            flags = \"wh\"  # h for header\n+        if self._outputFile:\n+            fileString = self._outputFile\n+        else:\n+            fileString = \"-\"\n+        alignmentFile = pysam.AlignmentFile(\n+            fileString, flags, header=header.toDict())\n+        iterator = self._httpClient.searchReads(self._searchReadsRequest)\n+        for read in iterator:\n+            alignedSegment = SamLine.toAlignedSegment(read, targetIds)\n+            alignmentFile.write(alignedSegment)\n+        alignmentFile.close()\n+\n+    def _getHeader(self):\n+        header = SamHeader()\n+        return header\n+\n+    def _getTargetIds(self, header):\n+        # this seems to be how pysam sets the target ids\n+        headerDict = header.toDict()\n+        targetIds = collections.defaultdict(int)\n+        targetId = 0\n+        if 'SQ' in headerDict:\n+            headerLines = headerDict['SQ']\n+            for headerLine in headerLines:\n+                refName = headerLine['SN']\n+                targetIds[refName] = targetId\n+                targetId += 1\n+        return targetIds\n+\n+\n+class SamHeader(object):\n+    \"\"\"\n+    The header of a SAM file\n+    \"\"\"\n+    def __init__(self):\n+        # TODO where to get actual values for header?\n+        # need some kind of getReadGroup(readGroupId) method in protocol\n+        # just add these dummy lines for now\n+        self.header = {\n+            'HD': {'VN': '1.0'},\n+            'SQ': [\n+                {'LN': 1575, 'SN': 'chr1'},\n+                {'LN': 1584, 'SN': 'chr2'},\n+            ],\n+        }\n+\n+    def toDict(self):\n+        return self.header\n+\n+\n+class SamLine(object):\n+    \"\"\"\n+    Methods for processing a line in a SAM file\n+    \"\"\"\n+    # see http://pysam.readthedocs.org/en/latest/api.html\n+    # #pysam.AlignedSegment.cigartuples\n+    cigarMap = {\n+        protocol.GACigarOperation.ALIGNMENT_MATCH: 0,\n+        protocol.GACigarOperation.INSERT: 1,\n+        protocol.GACigarOperation.DELETE: 2,\n+        protocol.GACigarOperation.SKIP: 3,\n+        protocol.GACigarOperation.CLIP_SOFT: 4,\n+        protocol.GACigarOperation.CLIP_HARD: 5,\n+        protocol.GACigarOperation.PAD: 6,\n+        protocol.GACigarOperation.SEQUENCE_MATCH: 7,\n+        protocol.GACigarOperation.SEQUENCE_MISMATCH: 8,\n+    }\n+\n+    # see tables in SAM spec, section 1.5\n+    _tagReservedFieldPrefixes = set([\"X\", \"Y\", \"Z\", ])\n+    _tagIntegerFields = set([\n+        \"AM\", \"AS\", \"CM\", \"CP\", \"FI\", \"H0\", \"H1\", \"H2\", \"HI\", \"IH\", \"MQ\",\n+        \"NH\", \"NM\", \"OP\", \"PQ\", \"SM\", \"TC\", \"UQ\", ])\n+    _tagStringFields = set([\n+        \"BC\", \"BQ\", \"CC\", \"CO\", \"CQ\", \"CS\", \"CT\", \"E2\", \"FS\", \"LB\", \"MC\",\n+        \"MD\", \"OQ\", \"OC\", \"PG\", \"PT\", \"PU\", \"QT\", \"Q2\", \"R2\", \"RG\", \"RT\",\n+        \"SA\", \"U2\", ])\n+    _tagIntegerArrayFields = set([\"FZ\", ])\n+\n+    def __init__(self):\n+        raise SamException(\"SamLine can't be instantiated\")\n+\n+    @classmethod\n+    def toAlignedSegment(cls, read, targetIds):\n+        alignedSegment = pysam.AlignedSegment()\n+        # QNAME\n+        alignedSegment.query_name = \\\n+            read.fragmentName.encode('utf8')","path":"ga4gh/converters.py","position":144,"original_position":144,"commit_id":"1e1cb120ad298915e3d7cf941c27616ea56dee1d","original_commit_id":"1e1cb120ad298915e3d7cf941c27616ea56dee1d","user":{"login":"jeromekelleher","id":2664569,"avatar_url":"https://avatars.githubusercontent.com/u/2664569?v=3","gravatar_id":"","url":"https://api.github.com/users/jeromekelleher","html_url":"https://github.com/jeromekelleher","followers_url":"https://api.github.com/users/jeromekelleher/followers","following_url":"https://api.github.com/users/jeromekelleher/following{/other_user}","gists_url":"https://api.github.com/users/jeromekelleher/gists{/gist_id}","starred_url":"https://api.github.com/users/jeromekelleher/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jeromekelleher/subscriptions","organizations_url":"https://api.github.com/users/jeromekelleher/orgs","repos_url":"https://api.github.com/users/jeromekelleher/repos","events_url":"https://api.github.com/users/jeromekelleher/events{/privacy}","received_events_url":"https://api.github.com/users/jeromekelleher/received_events","type":"User","site_admin":false},"body":"Make a class variable `_encoding` and set this to 'utf8'.  I find these line breaks really ugly and difficult to read also. Can we use a short variable name here? What about `ret`? I.e.\r\n```\r\nret = pysam.AlignedSegment()\r\nret.query_name = read.fragmentName.encode(self._encoding)\r\n#etc\r\n```\r\nSince the method name is `toAlignedSegment` this should be abundantly clear?","created_at":"2015-03-03T11:09:04Z","updated_at":"2015-03-03T11:09:04Z","html_url":"https://github.com/ga4gh/server/pull/201#discussion_r25678837","pull_request_url":"https://api.github.com/repos/ga4gh/server/pulls/201","_links":{"self":{"href":"https://api.github.com/repos/ga4gh/server/pulls/comments/25678837"},"html":{"href":"https://github.com/ga4gh/server/pull/201#discussion_r25678837"},"pull_request":{"href":"https://api.github.com/repos/ga4gh/server/pulls/201"}}},"pull_request":{"url":"https://api.github.com/repos/ga4gh/server/pulls/201","id":30305401,"html_url":"https://github.com/ga4gh/server/pull/201","diff_url":"https://github.com/ga4gh/server/pull/201.diff","patch_url":"https://github.com/ga4gh/server/pull/201.patch","issue_url":"https://api.github.com/repos/ga4gh/server/issues/201","number":201,"state":"open","locked":false,"title":"Add ga2sam tool","user":{"login":"dcolligan","id":7422120,"avatar_url":"https://avatars.githubusercontent.com/u/7422120?v=3","gravatar_id":"","url":"https://api.github.com/users/dcolligan","html_url":"https://github.com/dcolligan","followers_url":"https://api.github.com/users/dcolligan/followers","following_url":"https://api.github.com/users/dcolligan/following{/other_user}","gists_url":"https://api.github.com/users/dcolligan/gists{/gist_id}","starred_url":"https://api.github.com/users/dcolligan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dcolligan/subscriptions","organizations_url":"https://api.github.com/users/dcolligan/orgs","repos_url":"https://api.github.com/users/dcolligan/repos","events_url":"https://api.github.com/users/dcolligan/events{/privacy}","received_events_url":"https://api.github.com/users/dcolligan/received_events","type":"User","site_admin":false},"body":"Issue #83","created_at":"2015-03-02T18:02:41Z","updated_at":"2015-03-03T11:09:04Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6bf7908a38a8fc6aa774d7576a5262b99f4e93aa","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ga4gh/server/pulls/201/commits","review_comments_url":"https://api.github.com/repos/ga4gh/server/pulls/201/comments","review_comment_url":"https://api.github.com/repos/ga4gh/server/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ga4gh/server/issues/201/comments","statuses_url":"https://api.github.com/repos/ga4gh/server/statuses/1e1cb120ad298915e3d7cf941c27616ea56dee1d","head":{"label":"dcolligan:83_ga2sam","ref":"83_ga2sam","sha":"1e1cb120ad298915e3d7cf941c27616ea56dee1d","user":{"login":"dcolligan","id":7422120,"avatar_url":"https://avatars.githubusercontent.com/u/7422120?v=3","gravatar_id":"","url":"https://api.github.com/users/dcolligan","html_url":"https://github.com/dcolligan","followers_url":"https://api.github.com/users/dcolligan/followers","following_url":"https://api.github.com/users/dcolligan/following{/other_user}","gists_url":"https://api.github.com/users/dcolligan/gists{/gist_id}","starred_url":"https://api.github.com/users/dcolligan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dcolligan/subscriptions","organizations_url":"https://api.github.com/users/dcolligan/orgs","repos_url":"https://api.github.com/users/dcolligan/repos","events_url":"https://api.github.com/users/dcolligan/events{/privacy}","received_events_url":"https://api.github.com/users/dcolligan/received_events","type":"User","site_admin":false},"repo":{"id":29265238,"name":"server","full_name":"dcolligan/server","owner":{"login":"dcolligan","id":7422120,"avatar_url":"https://avatars.githubusercontent.com/u/7422120?v=3","gravatar_id":"","url":"https://api.github.com/users/dcolligan","html_url":"https://github.com/dcolligan","followers_url":"https://api.github.com/users/dcolligan/followers","following_url":"https://api.github.com/users/dcolligan/following{/other_user}","gists_url":"https://api.github.com/users/dcolligan/gists{/gist_id}","starred_url":"https://api.github.com/users/dcolligan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dcolligan/subscriptions","organizations_url":"https://api.github.com/users/dcolligan/orgs","repos_url":"https://api.github.com/users/dcolligan/repos","events_url":"https://api.github.com/users/dcolligan/events{/privacy}","received_events_url":"https://api.github.com/users/dcolligan/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dcolligan/server","description":"A reference implementation of the APIs defined in the schemas repository.","fork":true,"url":"https://api.github.com/repos/dcolligan/server","forks_url":"https://api.github.com/repos/dcolligan/server/forks","keys_url":"https://api.github.com/repos/dcolligan/server/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dcolligan/server/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dcolligan/server/teams","hooks_url":"https://api.github.com/repos/dcolligan/server/hooks","issue_events_url":"https://api.github.com/repos/dcolligan/server/issues/events{/number}","events_url":"https://api.github.com/repos/dcolligan/server/events","assignees_url":"https://api.github.com/repos/dcolligan/server/assignees{/user}","branches_url":"https://api.github.com/repos/dcolligan/server/branches{/branch}","tags_url":"https://api.github.com/repos/dcolligan/server/tags","blobs_url":"https://api.github.com/repos/dcolligan/server/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dcolligan/server/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dcolligan/server/git/refs{/sha}","trees_url":"https://api.github.com/repos/dcolligan/server/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dcolligan/server/statuses/{sha}","languages_url":"https://api.github.com/repos/dcolligan/server/languages","stargazers_url":"https://api.github.com/repos/dcolligan/server/stargazers","contributors_url":"https://api.github.com/repos/dcolligan/server/contributors","subscribers_url":"https://api.github.com/repos/dcolligan/server/subscribers","subscription_url":"https://api.github.com/repos/dcolligan/server/subscription","commits_url":"https://api.github.com/repos/dcolligan/server/commits{/sha}","git_commits_url":"https://api.github.com/repos/dcolligan/server/git/commits{/sha}","comments_url":"https://api.github.com/repos/dcolligan/server/comments{/number}","issue_comment_url":"https://api.github.com/repos/dcolligan/server/issues/comments{/number}","contents_url":"https://api.github.com/repos/dcolligan/server/contents/{+path}","compare_url":"https://api.github.com/repos/dcolligan/server/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dcolligan/server/merges","archive_url":"https://api.github.com/repos/dcolligan/server/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dcolligan/server/downloads","issues_url":"https://api.github.com/repos/dcolligan/server/issues{/number}","pulls_url":"https://api.github.com/repos/dcolligan/server/pulls{/number}","milestones_url":"https://api.github.com/repos/dcolligan/server/milestones{/number}","notifications_url":"https://api.github.com/repos/dcolligan/server/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dcolligan/server/labels{/name}","releases_url":"https://api.github.com/repos/dcolligan/server/releases{/id}","created_at":"2015-01-14T20:53:47Z","updated_at":"2015-03-02T16:09:37Z","pushed_at":"2015-03-02T18:54:41Z","git_url":"git://github.com/dcolligan/server.git","ssh_url":"git@github.com:dcolligan/server.git","clone_url":"https://github.com/dcolligan/server.git","svn_url":"https://github.com/dcolligan/server","homepage":null,"size":1184,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"ga4gh:master","ref":"master","sha":"1466311ba06bed0cabcc27601a9f61043538fd85","user":{"login":"ga4gh","id":6921777,"avatar_url":"https://avatars.githubusercontent.com/u/6921777?v=3","gravatar_id":"","url":"https://api.github.com/users/ga4gh","html_url":"https://github.com/ga4gh","followers_url":"https://api.github.com/users/ga4gh/followers","following_url":"https://api.github.com/users/ga4gh/following{/other_user}","gists_url":"https://api.github.com/users/ga4gh/gists{/gist_id}","starred_url":"https://api.github.com/users/ga4gh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ga4gh/subscriptions","organizations_url":"https://api.github.com/users/ga4gh/orgs","repos_url":"https://api.github.com/users/ga4gh/repos","events_url":"https://api.github.com/users/ga4gh/events{/privacy}","received_events_url":"https://api.github.com/users/ga4gh/received_events","type":"Organization","site_admin":false},"repo":{"id":22882621,"name":"server","full_name":"ga4gh/server","owner":{"login":"ga4gh","id":6921777,"avatar_url":"https://avatars.githubusercontent.com/u/6921777?v=3","gravatar_id":"","url":"https://api.github.com/users/ga4gh","html_url":"https://github.com/ga4gh","followers_url":"https://api.github.com/users/ga4gh/followers","following_url":"https://api.github.com/users/ga4gh/following{/other_user}","gists_url":"https://api.github.com/users/ga4gh/gists{/gist_id}","starred_url":"https://api.github.com/users/ga4gh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ga4gh/subscriptions","organizations_url":"https://api.github.com/users/ga4gh/orgs","repos_url":"https://api.github.com/users/ga4gh/repos","events_url":"https://api.github.com/users/ga4gh/events{/privacy}","received_events_url":"https://api.github.com/users/ga4gh/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ga4gh/server","description":"A reference implementation of the APIs defined in the schemas repository.","fork":false,"url":"https://api.github.com/repos/ga4gh/server","forks_url":"https://api.github.com/repos/ga4gh/server/forks","keys_url":"https://api.github.com/repos/ga4gh/server/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ga4gh/server/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ga4gh/server/teams","hooks_url":"https://api.github.com/repos/ga4gh/server/hooks","issue_events_url":"https://api.github.com/repos/ga4gh/server/issues/events{/number}","events_url":"https://api.github.com/repos/ga4gh/server/events","assignees_url":"https://api.github.com/repos/ga4gh/server/assignees{/user}","branches_url":"https://api.github.com/repos/ga4gh/server/branches{/branch}","tags_url":"https://api.github.com/repos/ga4gh/server/tags","blobs_url":"https://api.github.com/repos/ga4gh/server/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ga4gh/server/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ga4gh/server/git/refs{/sha}","trees_url":"https://api.github.com/repos/ga4gh/server/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ga4gh/server/statuses/{sha}","languages_url":"https://api.github.com/repos/ga4gh/server/languages","stargazers_url":"https://api.github.com/repos/ga4gh/server/stargazers","contributors_url":"https://api.github.com/repos/ga4gh/server/contributors","subscribers_url":"https://api.github.com/repos/ga4gh/server/subscribers","subscription_url":"https://api.github.com/repos/ga4gh/server/subscription","commits_url":"https://api.github.com/repos/ga4gh/server/commits{/sha}","git_commits_url":"https://api.github.com/repos/ga4gh/server/git/commits{/sha}","comments_url":"https://api.github.com/repos/ga4gh/server/comments{/number}","issue_comment_url":"https://api.github.com/repos/ga4gh/server/issues/comments{/number}","contents_url":"https://api.github.com/repos/ga4gh/server/contents/{+path}","compare_url":"https://api.github.com/repos/ga4gh/server/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ga4gh/server/merges","archive_url":"https://api.github.com/repos/ga4gh/server/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ga4gh/server/downloads","issues_url":"https://api.github.com/repos/ga4gh/server/issues{/number}","pulls_url":"https://api.github.com/repos/ga4gh/server/pulls{/number}","milestones_url":"https://api.github.com/repos/ga4gh/server/milestones{/number}","notifications_url":"https://api.github.com/repos/ga4gh/server/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ga4gh/server/labels{/name}","releases_url":"https://api.github.com/repos/ga4gh/server/releases{/id}","created_at":"2014-08-12T15:31:49Z","updated_at":"2015-03-02T19:09:36Z","pushed_at":"2015-03-02T16:01:56Z","git_url":"git://github.com/ga4gh/server.git","ssh_url":"git@github.com:ga4gh/server.git","clone_url":"https://github.com/ga4gh/server.git","svn_url":"https://github.com/ga4gh/server","homepage":null,"size":2653,"stargazers_count":25,"watchers_count":25,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":23,"mirror_url":null,"open_issues_count":42,"forks":23,"open_issues":42,"watchers":25,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ga4gh/server/pulls/201"},"html":{"href":"https://github.com/ga4gh/server/pull/201"},"issue":{"href":"https://api.github.com/repos/ga4gh/server/issues/201"},"comments":{"href":"https://api.github.com/repos/ga4gh/server/issues/201/comments"},"review_comments":{"href":"https://api.github.com/repos/ga4gh/server/pulls/201/comments"},"review_comment":{"href":"https://api.github.com/repos/ga4gh/server/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ga4gh/server/pulls/201/commits"},"statuses":{"href":"https://api.github.com/repos/ga4gh/server/statuses/1e1cb120ad298915e3d7cf941c27616ea56dee1d"}}}}