├── LICENSE ├── README.md ├── api-client-javascript ├── .gitignore ├── CONTRIBUTING.rst ├── LICENSE ├── README.md ├── googlegenomics.jquery.js └── traitviewer │ ├── index.html │ └── traits.json ├── api-client-python ├── CONTRIBUTING.rst ├── LICENSE ├── README.rst ├── app.yaml ├── httplib2 │ ├── __init__.py │ ├── cacerts.txt │ ├── iri2uri.py │ └── socks.py ├── localserver.py ├── main.html ├── main.py └── static │ ├── css │ ├── bootstrap.min.css │ └── main.css │ ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff │ ├── img │ ├── chr1.png │ ├── chr10.png │ ├── chr11.png │ ├── chr12.png │ ├── chr13.png │ ├── chr14.png │ ├── chr15.png │ ├── chr16.png │ ├── chr17.png │ ├── chr18.png │ ├── chr19.png │ ├── chr2.png │ ├── chr20.png │ ├── chr21.png │ ├── chr22.png │ ├── chr3.png │ ├── chr4.png │ ├── chr5.png │ ├── chr6.png │ ├── chr7.png │ ├── chr8.png │ ├── chr9.png │ ├── chrX.png │ ├── chrY.png │ ├── spinner.gif │ ├── zoom-bar.png │ ├── zoom-level.png │ ├── zoom-minus.png │ └── zoom-plus.png │ ├── js │ ├── bootstrap.min.js │ ├── d3.v3.min.js │ ├── jquery.bootpag.min.js │ ├── main.js │ ├── rbtree.js │ ├── readcache.js │ ├── readgraph.js │ └── underscore-min.js │ └── robots.txt ├── gatk-tools-java ├── .gitignore ├── CONTRIBUTING.rst ├── LICENSE ├── README.md ├── build.xml ├── lib │ ├── genomics-tools-client-java-v1beta.jar │ ├── google-genomics-utils-v1beta2-0.20-SNAPSHOT.jar │ ├── guava-18.0.jar │ └── htsjdk-1.129.jar ├── pom.xml ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── google │ │ │ └── cloud │ │ │ └── genomics │ │ │ └── gatk │ │ │ ├── common │ │ │ ├── GA4GHUrl.java │ │ │ ├── GenomicsApiDataSource.java │ │ │ ├── GenomicsApiDataSourceFactory.java │ │ │ ├── GenomicsConverter.java │ │ │ ├── ReadIteratorResource.java │ │ │ └── UnmappedReads.java │ │ │ ├── htsjdk │ │ │ ├── GA4GHQueryInterval.java │ │ │ ├── GA4GHReaderFactory.java │ │ │ ├── GA4GHSamReader.java │ │ │ ├── GA4GHSamRecordIterator.java │ │ │ └── SamReaderExample.java │ │ │ └── picard │ │ │ └── runner │ │ │ ├── GA4GHPicardRunner.java │ │ │ ├── ReadIteratorToSAMFilePump.java │ │ │ ├── SAMFilePump.java │ │ │ └── SamReaderToSAMFilePump.java │ │ └── scripts │ │ ├── mark_duplicates.sh │ │ ├── mark_duplicates_cigar.sh │ │ ├── run_picard.sh │ │ └── view_sam_file.sh └── testdata │ ├── ex1_sorted.bam │ ├── ex1_sorted.sam │ ├── unmapped_mate.bam │ └── unmapped_mate.sam └── transcoders ├── db.sqlite3 ├── hmdna ├── __init__.py ├── __init__.pyc ├── settings.py ├── settings.pyc ├── urls.py ├── urls.pyc ├── wsgi.py └── wsgi.pyc └── manage.py /LICENSE: -------------------------------------------------------------------------------- 1 | The Artistic License 2.0 2 | 3 | Copyright (c) 2015 bcl-io 4 | 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | This license establishes the terms under which a given free software 11 | Package may be copied, modified, distributed, and/or redistributed. 12 | The intent is that the Copyright Holder maintains some artistic 13 | control over the development of that Package while still keeping the 14 | Package available as open source and free software. 15 | 16 | You are always permitted to make arrangements wholly outside of this 17 | license directly with the Copyright Holder of a given Package. If the 18 | terms of this license do not permit the full use that you propose to 19 | make of the Package, you should contact the Copyright Holder and seek 20 | a different licensing arrangement. 21 | 22 | Definitions 23 | 24 | "Copyright Holder" means the individual(s) or organization(s) 25 | named in the copyright notice for the entire Package. 26 | 27 | "Contributor" means any party that has contributed code or other 28 | material to the Package, in accordance with the Copyright Holder's 29 | procedures. 30 | 31 | "You" and "your" means any person who would like to copy, 32 | distribute, or modify the Package. 33 | 34 | "Package" means the collection of files distributed by the 35 | Copyright Holder, and derivatives of that collection and/or of 36 | those files. A given Package may consist of either the Standard 37 | Version, or a Modified Version. 38 | 39 | "Distribute" means providing a copy of the Package or making it 40 | accessible to anyone else, or in the case of a company or 41 | organization, to others outside of your company or organization. 42 | 43 | "Distributor Fee" means any fee that you charge for Distributing 44 | this Package or providing support for this Package to another 45 | party. It does not mean licensing fees. 46 | 47 | "Standard Version" refers to the Package if it has not been 48 | modified, or has been modified only in ways explicitly requested 49 | by the Copyright Holder. 50 | 51 | "Modified Version" means the Package, if it has been changed, and 52 | such changes were not explicitly requested by the Copyright 53 | Holder. 54 | 55 | "Original License" means this Artistic License as Distributed with 56 | the Standard Version of the Package, in its current version or as 57 | it may be modified by The Perl Foundation in the future. 58 | 59 | "Source" form means the source code, documentation source, and 60 | configuration files for the Package. 61 | 62 | "Compiled" form means the compiled bytecode, object code, binary, 63 | or any other form resulting from mechanical transformation or 64 | translation of the Source form. 65 | 66 | 67 | Permission for Use and Modification Without Distribution 68 | 69 | (1) You are permitted to use the Standard Version and create and use 70 | Modified Versions for any purpose without restriction, provided that 71 | you do not Distribute the Modified Version. 72 | 73 | 74 | Permissions for Redistribution of the Standard Version 75 | 76 | (2) You may Distribute verbatim copies of the Source form of the 77 | Standard Version of this Package in any medium without restriction, 78 | either gratis or for a Distributor Fee, provided that you duplicate 79 | all of the original copyright notices and associated disclaimers. At 80 | your discretion, such verbatim copies may or may not include a 81 | Compiled form of the Package. 82 | 83 | (3) You may apply any bug fixes, portability changes, and other 84 | modifications made available from the Copyright Holder. The resulting 85 | Package will still be considered the Standard Version, and as such 86 | will be subject to the Original License. 87 | 88 | 89 | Distribution of Modified Versions of the Package as Source 90 | 91 | (4) You may Distribute your Modified Version as Source (either gratis 92 | or for a Distributor Fee, and with or without a Compiled form of the 93 | Modified Version) provided that you clearly document how it differs 94 | from the Standard Version, including, but not limited to, documenting 95 | any non-standard features, executables, or modules, and provided that 96 | you do at least ONE of the following: 97 | 98 | (a) make the Modified Version available to the Copyright Holder 99 | of the Standard Version, under the Original License, so that the 100 | Copyright Holder may include your modifications in the Standard 101 | Version. 102 | 103 | (b) ensure that installation of your Modified Version does not 104 | prevent the user installing or running the Standard Version. In 105 | addition, the Modified Version must bear a name that is different 106 | from the name of the Standard Version. 107 | 108 | (c) allow anyone who receives a copy of the Modified Version to 109 | make the Source form of the Modified Version available to others 110 | under 111 | 112 | (i) the Original License or 113 | 114 | (ii) a license that permits the licensee to freely copy, 115 | modify and redistribute the Modified Version using the same 116 | licensing terms that apply to the copy that the licensee 117 | received, and requires that the Source form of the Modified 118 | Version, and of any works derived from it, be made freely 119 | available in that license fees are prohibited but Distributor 120 | Fees are allowed. 121 | 122 | 123 | Distribution of Compiled Forms of the Standard Version 124 | or Modified Versions without the Source 125 | 126 | (5) You may Distribute Compiled forms of the Standard Version without 127 | the Source, provided that you include complete instructions on how to 128 | get the Source of the Standard Version. Such instructions must be 129 | valid at the time of your distribution. If these instructions, at any 130 | time while you are carrying out such distribution, become invalid, you 131 | must provide new instructions on demand or cease further distribution. 132 | If you provide valid instructions or cease distribution within thirty 133 | days after you become aware that the instructions are invalid, then 134 | you do not forfeit any of your rights under this license. 135 | 136 | (6) You may Distribute a Modified Version in Compiled form without 137 | the Source, provided that you comply with Section 4 with respect to 138 | the Source of the Modified Version. 139 | 140 | 141 | Aggregating or Linking the Package 142 | 143 | (7) You may aggregate the Package (either the Standard Version or 144 | Modified Version) with other packages and Distribute the resulting 145 | aggregation provided that you do not charge a licensing fee for the 146 | Package. Distributor Fees are permitted, and licensing fees for other 147 | components in the aggregation are permitted. The terms of this license 148 | apply to the use and Distribution of the Standard or Modified Versions 149 | as included in the aggregation. 150 | 151 | (8) You are permitted to link Modified and Standard Versions with 152 | other works, to embed the Package in a larger work of your own, or to 153 | build stand-alone binary or bytecode versions of applications that 154 | include the Package, and Distribute the result without restriction, 155 | provided the result does not expose a direct interface to the Package. 156 | 157 | 158 | Items That are Not Considered Part of a Modified Version 159 | 160 | (9) Works (including, but not limited to, modules and scripts) that 161 | merely extend or make use of the Package, do not, by themselves, cause 162 | the Package to be a Modified Version. In addition, such works are not 163 | considered parts of the Package itself, and are not subject to the 164 | terms of this license. 165 | 166 | 167 | General Provisions 168 | 169 | (10) Any use, modification, and distribution of the Standard or 170 | Modified Versions is governed by this Artistic License. By using, 171 | modifying or distributing the Package, you accept this license. Do not 172 | use, modify, or distribute the Package, if you do not accept this 173 | license. 174 | 175 | (11) If your Modified Version has been derived from a Modified 176 | Version made by someone other than you, you are nevertheless required 177 | to ensure that your Modified Version complies with the requirements of 178 | this license. 179 | 180 | (12) This license does not grant you the right to use any trademark, 181 | service mark, tradename, or logo of the Copyright Holder. 182 | 183 | (13) This license includes the non-exclusive, worldwide, 184 | free-of-charge patent license to make, have made, use, offer to sell, 185 | sell, import and otherwise transfer the Package with respect to any 186 | patent claims licensable by the Copyright Holder that are necessarily 187 | infringed by the Package. If you institute patent litigation 188 | (including a cross-claim or counterclaim) against any party alleging 189 | that the Package constitutes direct or contributory patent 190 | infringement, then this Artistic License to you shall terminate on the 191 | date that such litigation is filed. 192 | 193 | (14) Disclaimer of Warranty: 194 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS 195 | IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED 196 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 197 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL 198 | LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL 199 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 200 | DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF 201 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #hmDNA 2 | 3 | ##Part of the upcoming *Ghost in the Cell* project, coming to the [21st Century Museum of Contemporary Art](https://www.kanazawa21/) in Kanazawa, Japan in September 2015. 4 | 5 | DNA is the carrier of our genetic information, it defines our biological bodies - we share DNA with all other living creatures on earth. It's what makes us unique, and at the same time connects us to all other living beings. 6 | 7 | The mission of the hmDNA Project to create a fully synthetic genome for a virtual existing person. This is not a rational process. We don't expect the resulting genome to be a perfect match, but we expected the process of creating this genome to be a playful e 8 | 9 | 10 | ### 1. Source Data 11 | Use publicly available japanese genome from the [1000 Genomes Project](http://www.1000genomes.org) as a data source. 12 | 13 | ### 2. Functional Backbone 14 | Search for coding and functional non-coding regions and copy them to the new hmGenome. This will ensure that the resulting genome could be potentially functioning, it will make sure that there is enough free space that can be occupied by other data. 15 | 16 | ### 3. hmDNAgents 17 | Create *hmDNAgents* that copy different types of data into the hmGenome. *hmDNAgents* come in different flavours: 18 | * Transcribers: specific rules to copy DNA data from existing sequence data. 19 | * Translators: use gene annotation and gene ontology terms to define and insert sequences. 20 | * Transcoders: convert and insert any type of source media - images, text, sound, etc. 21 | 22 | ### 4. API 23 | The *hmDNAgents* can be accessed through an RESTful API, which allows the creation of client apps, applications web-interfaces or devices. 24 | 25 | ### 5. Participation 26 | Over the next weeks on months we will create a range of backend *hmDNAgents* and frontend interfaces to facilitate the generation of the hmGenome. 27 | 28 | 29 | ### Questions 30 | 31 | Any questions? Send them to @bcl_io #hmDNA 32 | -------------------------------------------------------------------------------- /api-client-javascript/.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /api-client-javascript/CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | How to contribute 2 | =================================== 3 | 4 | First of all, thank you for contributing! 5 | 6 | The mailing list 7 | ---------------- 8 | 9 | For general questions or if you are having trouble getting started, try the 10 | `Google Genomics Discuss mailing list `_. 11 | It's a good way to sync up with other people who use googlegenomics including the core developers. You can subscribe 12 | by sending an email to ``google-genomics-discuss+subscribe@googlegroups.com`` or just post using 13 | the `web forum page `_. 14 | 15 | 16 | Submitting issues 17 | ----------------- 18 | 19 | If you are encountering a bug in the code or have a feature request in mind - file away! 20 | 21 | 22 | Submitting a pull request 23 | ------------------------- 24 | 25 | If you are ready to contribute code, Github provides a nice `overview on how to create a pull request 26 | `_. 27 | 28 | Some general rules to follow: 29 | 30 | * Do your work in `a fork `_ of this repo. 31 | * Create a branch for each update that you're working on. 32 | These branches are often called "feature" or "topic" branches. Any changes 33 | that you push to your feature branch will automatically be shown in the pull request. 34 | * Keep your pull requests as small as possible. Large pull requests are hard to review. 35 | Try to break up your changes into self-contained and incremental pull requests. 36 | * The first line of commit messages should be a short (<80 character) summary, 37 | followed by an empty line and then any details that you want to share about the commit. 38 | * Please try to follow the existing syntax style 39 | 40 | When you submit or change your pull request, the Travis build system will automatically run tests. 41 | If your pull request fails to pass tests, review the test log, make changes and 42 | then push them to your feature branch to be tested again. 43 | 44 | 45 | Contributor License Agreements 46 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 47 | 48 | All pull requests are welcome. Before we can submit them though, there is a legal hurdle we have to jump. 49 | You'll need to fill out either the individual or corporate Contributor License Agreement 50 | (CLA). 51 | 52 | * If you are an individual writing original source code and you're sure you 53 | own the intellectual property, then you'll need to sign an `individual CLA 54 | `_. 55 | * If you work for a company that wants to allow you to contribute your work, 56 | then you'll need to sign a `corporate CLA 57 | `_. 58 | 59 | Follow either of the two links above to access the appropriate CLA and 60 | instructions for how to sign and return it. Once we receive it, we'll be able to 61 | accept your pull requests. 62 | -------------------------------------------------------------------------------- /api-client-javascript/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /api-client-javascript/README.md: -------------------------------------------------------------------------------- 1 | api-client-javascript 2 | ===================== 3 | 4 | ## Getting started 5 | 6 | There are html and js files in this repository. 7 | You can open the `index.html` files in your browser directly, but the javascript APIs won't work unless 8 | the HTML is hosted somewhere. (The Bootstrap css won't load from a `file://` prefix either) 9 | 10 | To run a simple HTTP server locally, you can use python: 11 | ``` 12 | cd api-client-javascript 13 | python -m SimpleHTTPServer 8000 14 | ``` 15 | 16 | This will start a local server. Visit `http://localhost:8000/traitviewer` 17 | to see the javascript example. 18 | 19 | To get data from the API, you will also need to use a real Client ID. 20 | 21 | * First create a [Genomics enabled project](https://console.developers.google.com/flows/enableapi?apiid=genomics) 22 | in the Google Developers Console. 23 | 24 | * Once you are redirected to the **Credentials** tab, click **Create new Client ID** under 25 | the OAuth section. 26 | 27 | * Set **Application type** to **Web application**, and change 28 | the **Authorized javascript origins** to `http://localhost:8000` 29 | 30 | * Click the **Create Client ID** button 31 | 32 | * From the newly created **Client ID for web application**, find the `Client ID` 33 | value. 34 | 35 | * Using that Client ID value, load the code at: 36 | `http://localhost:8000/traitviewer#your-client-id-goes-here` 37 | 38 | 39 | Note: If you want to run the code on any other domain, make sure you update the 40 | javascript origins on your Client ID to include that new domain. 41 | 42 | 43 | ## Code layout 44 | 45 | * traitviewer/index.html: 46 | 47 | loads [Bootstrap](getbootstrap.com) and [jQuery](http://jquery.com/) 48 | 49 | The file contains some simple html construction based on the `traits` json variable. 50 | It then uses `googlegenomics.jquery.js` to search variants and lookup 51 | genotype information for a callset. 52 | 53 | * googlegenomics.jquery.js: 54 | 55 | this is a work-in-progress jQuery plugin that makes fetching data from the 56 | [Genomics API](http://cloud.google.com/genomics) a bit easier. It wraps 57 | [Google's javascript client library](https://developers.google.com/api-client-library/javascript/). 58 | 59 | 60 | ## Project status 61 | 62 | ### Goals 63 | 64 | * Provide an example of how to use the javascript client library. 65 | * Demonstrate how the variant APIs can be used to get call set data. 66 | 67 | ### Current status 68 | 69 | Code needs some cleanup, but not much else is planned at this time. 70 | -------------------------------------------------------------------------------- /api-client-javascript/googlegenomics.jquery.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * This jquery plugin wraps Google's javascript client library with 19 | * helper functions to make fetching genomics data a bit easier. 20 | * 21 | * To use, setup the genomics API at the top of your code: 22 | * 23 | * $.initGenomics({clientId: 'your-client-id-goes-here'}); 24 | * 25 | * Then get genomics data by using the authGenomics function with a callback: 26 | * 27 | * $.authGenomics(function() { 28 | * gapi.client.genomics.datasets.get(datasetId).execute(function(json) { 29 | * // Do something with the json that comes back 30 | * }); 31 | * }); 32 | * 33 | * The initGenomics function also supports the use of non-genomics scopes and 34 | * libraries through the options parameter. 35 | */ 36 | (function ($) { 37 | var settings; 38 | 39 | function authUser(invisibleAuth, callback) { 40 | var params = {client_id: settings.clientId, scope: settings.scopes, 41 | immediate: invisibleAuth}; 42 | gapi.auth.authorize(params, function(authResult) { 43 | checkAuth(authResult, invisibleAuth, callback); 44 | }); 45 | } 46 | 47 | function checkAuth(authResult, invisibleAuth, callback) { 48 | if (authResult && !authResult.error) { 49 | settings.userAuthorized = true; 50 | callback(); 51 | } else if (invisibleAuth) { 52 | callback(); 53 | } else { 54 | // Something went wrong with the auth flow 55 | throw new Error("Authorization failed"); 56 | } 57 | // Note: If the user simply closes the auth popup, we (apparently) 58 | // won't have any idea. 59 | // TODO: Add a timeout to this plugin which cancels the call 60 | // and supports an error callback 61 | } 62 | 63 | // Don't call this directly! It's necessary & only used by 64 | // the Google Javascript Client library. The client library also requires 65 | // this global scope definition. 66 | window['genomicsOnload'] = function() { 67 | $.each(settings.libraries, function(i, library) { 68 | // TODO: Pass a real callback and delay initCallback execution 69 | gapi.client.load(library.name, library.version, function() {}); 70 | }); 71 | 72 | authUser(true, settings.initCallback || function(){}); 73 | }; 74 | 75 | // Wraps jQuery's ajax with useful defaults - including an authorization 76 | // header, error handler, and json type setting. 77 | // 78 | // The path parameter is a relative genomics path, like '/readsets/search' 79 | // The correct base url will be prepended. 80 | // 81 | // When using this function, you do not need to make calls to $.authGenomics 82 | // nor use the initCallback option in $.initGenomics. 83 | // 84 | // Example usage: 85 | // $.initGenomics({clientId: clientId}); 86 | // $.genomicsAjax('/datasets/10473108253681171589', { 87 | // success: function(dataset) { alert("Dataset: " + dataset.name); } 88 | // }); 89 | // 90 | $.genomicsAjax = function(path, options) { 91 | var version = options.version || 'v1beta'; 92 | $.authGenomics(function() { 93 | $.ajax($.extend({ 94 | url: 'https://www.googleapis.com/genomics/' + version + path, 95 | contentType: 'application/json; charset=utf-8', 96 | dataType: 'json', 97 | beforeSend: function (request) { 98 | request.setRequestHeader('Authorization', 99 | 'Bearer ' + gapi.auth.getToken().access_token); 100 | }, 101 | error: function(xhr) { 102 | alert("API call failed: " + xhr.responseJSON.error.message); 103 | } 104 | }, options)) 105 | }); 106 | }; 107 | 108 | // This method asks the user for permission to read genomics data. 109 | // (If access has already been granted, then the asking will be invisible) 110 | // 111 | // Inside of the callback, use the gapi.client.genomics.* calls to fetch data. 112 | $.authGenomics = function(callback) { 113 | if (!settings) { 114 | throw new Error("$.initGenomics must be called first."); 115 | } 116 | if (settings.userAuthorized) { 117 | callback(); 118 | } else if (window['gapi']) { 119 | authUser(false, callback); 120 | } else { 121 | // The API hasn't loaded yet, queue the callback 122 | var oldCallback = settings.initCallback; 123 | var newCallback = function() { $.authGenomics(callback) }; 124 | settings.initCallback = !oldCallback ? newCallback : function() { 125 | newCallback(); 126 | oldCallback(); 127 | } 128 | } 129 | }; 130 | 131 | // This method must be called to setup the genomics apis. 132 | // This loads an additional script file, and calls the gapi load function. 133 | $.initGenomics = function(options) { 134 | settings = $.extend({ 135 | // These are the defaults. 136 | clientId: '', 137 | scopes: ['https://www.googleapis.com/auth/genomics'], 138 | libraries: [{name: 'genomics', version: 'v1beta'}], 139 | initCallback: null 140 | }, options); 141 | 142 | // Check the clientId field 143 | if (!settings.clientId || settings.clientId == 'your-client-id-goes-here') { 144 | alert('You need to provide a real clientId to use this code. ' + 145 | 'Check the README for more instructions.'); 146 | return; 147 | } 148 | 149 | $.getScript('https://apis.google.com/js/client.js?onload=genomicsOnload'); 150 | }; 151 | }(jQuery)); 152 | -------------------------------------------------------------------------------- /api-client-javascript/traitviewer/index.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 32 | 33 | 34 | 35 |
36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 | 45 |
46 |
47 | 48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
NameContigPositionAlleleCall set genotypeCall set score
68 | 70 |
71 |
72 |
73 | 74 | 75 | 229 | 230 | 231 | -------------------------------------------------------------------------------- /api-client-javascript/traitviewer/traits.json: -------------------------------------------------------------------------------- 1 | {"traits": [ 2 | 3 | {"name": "Red hair", "alleles": [ 4 | {"name": "rs34474212", "description": "Gene MC1R, normally C or T", 5 | "contig": "16", "position": 89985912, "allele": "C", "score": 1}, 6 | 7 | {"name": "rs1805006", "description": "Gene MC1R, normally A or C", 8 | "contig": "16", "position": 89985917, "allele": "A", "score": 1}, 9 | 10 | {"name": "rs11547464", "description": "Gene MC1R, normally A or G", 11 | "contig": "16", "position": 89986090, "allele": "A", "score": 1}, 12 | 13 | {"name": "rs1110400", "description": "Gene MC1R, normally C or T", 14 | "contig": "16", "position": 89986129, "allele": "C", "score": 1}, 15 | 16 | {"name": "rs1805007", "description": "Gene MC1R, normally C or G or T", 17 | "contig": "16", "position": 89986116, "allele": "T", "score": 1}, 18 | 19 | {"name": "rs1805008", "description": "Gene MC1R, normally C or T", 20 | "contig": "16", "position": 89986143, "allele": "T", "score": 1}, 21 | 22 | {"name": "i3002507", "description": "Gene MC1R, normally C or G", 23 | "contig": "16", "position": 89986545, "allele": "C", "score": 1} 24 | ], "scoreBands": [ 25 | {"score": 2, "description": "Probably has red hair"}, 26 | {"score": 0, "description": "Probably does not have red hair"} 27 | ] 28 | }, 29 | 30 | 31 | {"name": "Test trait", "alleles": [ 32 | {"contig": "22", "position": 16051476, "allele": "A", "score": 5}, 33 | {"contig": "22", "position": 16051476, "allele": "C", "score": 3} 34 | ], "scoreBands": [ 35 | {"score": 6, "description": "score greater than 6"}, 36 | {"score": 1, "description": "score greater than 1"}, 37 | {"score": 0, "description": "score greater than 0"} 38 | ] 39 | } 40 | ]} -------------------------------------------------------------------------------- /api-client-python/CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | How to contribute 2 | =================================== 3 | 4 | First of all, thank you for contributing! 5 | 6 | The mailing list 7 | ---------------- 8 | 9 | For general questions or if you are having trouble getting started, try the 10 | `Google Genomics Discuss mailing list `_. 11 | It's a good way to sync up with other people who use googlegenomics including the core developers. You can subscribe 12 | by sending an email to ``google-genomics-discuss+subscribe@googlegroups.com`` or just post using 13 | the `web forum page `_. 14 | 15 | 16 | Submitting issues 17 | ----------------- 18 | 19 | If you are encountering a bug in the code or have a feature request in mind - file away! 20 | 21 | 22 | Submitting a pull request 23 | ------------------------- 24 | 25 | If you are ready to contribute code, Github provides a nice `overview on how to create a pull request 26 | `_. 27 | 28 | Some general rules to follow: 29 | 30 | * Do your work in `a fork `_ of this repo. 31 | * Create a branch for each update that you're working on. 32 | These branches are often called "feature" or "topic" branches. Any changes 33 | that you push to your feature branch will automatically be shown in the pull request. 34 | * Keep your pull requests as small as possible. Large pull requests are hard to review. 35 | Try to break up your changes into self-contained and incremental pull requests. 36 | * The first line of commit messages should be a short (<80 character) summary, 37 | followed by an empty line and then any details that you want to share about the commit. 38 | * Please try to follow the existing syntax style 39 | 40 | When you submit or change your pull request, the Travis build system will automatically run tests. 41 | If your pull request fails to pass tests, review the test log, make changes and 42 | then push them to your feature branch to be tested again. 43 | 44 | 45 | Contributor License Agreements 46 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 47 | 48 | All pull requests are welcome. Before we can submit them though, there is a legal hurdle we have to jump. 49 | You'll need to fill out either the individual or corporate Contributor License Agreement 50 | (CLA). 51 | 52 | * If you are an individual writing original source code and you're sure you 53 | own the intellectual property, then you'll need to sign an `individual CLA 54 | `_. 55 | * If you work for a company that wants to allow you to contribute your work, 56 | then you'll need to sign a `corporate CLA 57 | `_. 58 | 59 | Follow either of the two links above to access the appropriate CLA and 60 | instructions for how to sign and return it. Once we receive it, we'll be able to 61 | accept your pull requests. 62 | -------------------------------------------------------------------------------- /api-client-python/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /api-client-python/README.rst: -------------------------------------------------------------------------------- 1 | api-client-python 2 | ================= 3 | 4 | Getting started 5 | --------------- 6 | 7 | This Python client demonstrates a simple web-based genome browser that fetches data from the 8 | `Google Genomics API`_, the NCBI Genomics API or the Local Readstore through a web 9 | interface, and displays a pileup of reads with support for zooming and basic navigation and search. 10 | 11 | You can try out the sample genome browser, called GABrowse, now by going to https://gabrowse.appspot.com. 12 | 13 | It can be run with app engine or without. 14 | See `the docs `_ 15 | for more information. 16 | 17 | .. _Google Genomics Api: https://cloud.google.com/genomics 18 | 19 | Running on App Engine 20 | ~~~~~~~~~~~~~~~~~~~~~ 21 | 22 | To run with app engine, you'll need to download and install `Google App Engine SDK for Python 23 | `_. 24 | 25 | On Mac OS X you can setup and run the application through the GoogleAppEngineLauncher UI. 26 | To use the command line or to run on Linux:: 27 | 28 | cd api-client-python 29 | dev_appserver.py . 30 | 31 | To run on Windows:: 32 | 33 | cd api-client-python 34 | python c:\path\to\dev_appserver.py . 35 | 36 | Once running, visit ``http://localhost:8080`` in your browser to browse data from the API. 37 | 38 | 39 | Running with paste and webapp2 40 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 41 | 42 | If you don't want to use App Engine, you can instead run the local server with paste. 43 | First you'll need to `install pip `_. 44 | 45 | Then install the required dependencies and run the ``localserver.py`` file:: 46 | 47 | pip install WebOb Paste webapp2 jinja2 48 | python localserver.py 49 | 50 | Enabling the Google API 51 | ~~~~~~~~~~~~~~~~~~~~~~~ 52 | 53 | If you want to pull in data from `Google Genomics API`_ you will need to store a valid 54 | Google API key into a file named ``google_api_key.txt``. 55 | 56 | * First create a `Genomics enabled project `_ 57 | in the Google Developers Console. 58 | 59 | * Once you are redirected to the **Credentials** tab, click **create new key** under 60 | the Public API access section. 61 | 62 | * Select **Server key** in the dialog that pops up, and then click **Create**. 63 | (You don't need to enter anything in the text box) 64 | 65 | * Copy the **API key** field value that now appears in the Public API access 66 | section into a new file named ``google_api_key.txt`` in the same directory as ``main.py``. 67 | The file should consist of a single undecorated line like this:: 68 | 69 | abcdef12345abcdef 70 | 71 | Note: You can also reuse an existing API key if you have one. 72 | Just make sure the Genomics API is turned on. 73 | 74 | 75 | Troubleshooting 76 | ~~~~~~~~~~~~~~~ 77 | 78 | * The ``google.appengine.tools.devappserver2.wsgi_server.BindError: Unable to bind`` message 79 | means that one of the default App Engine ports is unavailable. The default ports are 8080 and 8000. 80 | You can try different ports with these flags:: 81 | 82 | python dev_appserver.py --port 12080 --admin_port=12000 . 83 | 84 | Your server will then be available at ``localhost:12080``. 85 | 86 | * Problem with a non-Chrome browser? Please 87 | `file an issue `_. 88 | jQuery and d3 get us a lot of browser portability for free - 89 | but testing on all configurations is tricky, so just let us know 90 | if there are issues! 91 | 92 | Code layout 93 | ----------- 94 | 95 | main.py: 96 | queries the Genomics API and handles all OAuth flows. It also serves up the HTML 97 | pages. 98 | 99 | main.html: 100 | is the main HTML page. It is displayed once the user has granted OAuth access to 101 | the Genomics API. 102 | It provides the basic page layout, but most of the display logic is handled in 103 | JavaScript. 104 | 105 | static/js/main.js: 106 | provides some JS utility functions, and calls into ``readgraph.js``. 107 | 108 | static/js/readgraph.js: 109 | handles the visualization of reads. It contains the most complex code and uses 110 | `d3.js `_ to display actual Read data. 111 | 112 | The python client also depends on several external libraries: 113 | 114 | `httplib2`_: 115 | used to fetch data from API providers 116 | 117 | `D3`_: 118 | is a javascript library used to make rich visualizations 119 | 120 | `Underscore.js`_: 121 | is a javascript library that provides a variety of utilities 122 | 123 | `Bootstrap`_: 124 | supplies a great set of default css, icons, and js helpers 125 | 126 | In ``main.html``, `jQuery `_ is also loaded from an external 127 | site. 128 | 129 | .. _httplib2: https://github.com/jcgregorio/httplib2 130 | .. _D3: http://d3js.org 131 | .. _Underscore.js: http://underscorejs.org 132 | .. _Bootstrap: http://getbootstrap.com 133 | 134 | 135 | Project status 136 | -------------- 137 | 138 | Goals 139 | ~~~~~ 140 | * Provide an easily deployable demo that demonstrates what Genomics API interop 141 | can achieve for the community. 142 | * Provide an example of how to use the Genomics APIs and OAuth to build a 143 | non-trivial python application. 144 | 145 | 146 | Current status 147 | ~~~~~~~~~~~~~~ 148 | This code *wants* to be in active development, but has few contributions coming 149 | in at the moment. 150 | 151 | Currently, it provides a basic genome browser that can consume genomic data 152 | from any API provider. It deploys on App Engine (to meet the 153 | 'easily deployable' goal), and has a layman-friendly UI. 154 | 155 | Awesome possible features include: 156 | 157 | * Add more information to the read display (show inserts, highlight mismatches 158 | against the reference, etc) 159 | * Possibly cleaning up the js code to be more plugin friendly - so that pieces 160 | could be shared and reused (d3 library? jquery plugin?) 161 | * Staying up to date on API changes (readset searching now has pagination, etc) 162 | * Better searching of Snpedia (or another provider - EBI?) 163 | * Other enhancement ideas are very welcome 164 | * (for smaller/additional tasks see the GitHub issues) 165 | -------------------------------------------------------------------------------- /api-client-python/app.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Google Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | application: gabrowse 16 | version: 1 17 | runtime: python27 18 | api_version: 1 19 | threadsafe: yes 20 | 21 | handlers: 22 | 23 | # Static files 24 | - url: /static 25 | static_dir: static 26 | 27 | # Make robots.txt accessible at the root URL. 28 | - url: /robots.txt 29 | static_files: static/robots.txt 30 | upload: static/robots.txt 31 | 32 | # All other urls get handled by main.py 33 | - url: .* 34 | script: main.web_app 35 | 36 | # Third party libraries that are included in the App Engine SDK 37 | libraries: 38 | - name: jinja2 39 | version: 2.6 40 | - name: webapp2 41 | version: 2.5.2 42 | -------------------------------------------------------------------------------- /api-client-python/httplib2/iri2uri.py: -------------------------------------------------------------------------------- 1 | """ 2 | iri2uri 3 | 4 | Converts an IRI to a URI. 5 | 6 | """ 7 | __author__ = "Joe Gregorio (joe@bitworking.org)" 8 | __copyright__ = "Copyright 2006, Joe Gregorio" 9 | __contributors__ = [] 10 | __version__ = "1.0.0" 11 | __license__ = "MIT" 12 | __history__ = """ 13 | """ 14 | 15 | import urlparse 16 | 17 | 18 | # Convert an IRI to a URI following the rules in RFC 3987 19 | # 20 | # The characters we need to enocde and escape are defined in the spec: 21 | # 22 | # iprivate = %xE000-F8FF / %xF0000-FFFFD / %x100000-10FFFD 23 | # ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF 24 | # / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD 25 | # / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD 26 | # / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD 27 | # / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD 28 | # / %xD0000-DFFFD / %xE1000-EFFFD 29 | 30 | escape_range = [ 31 | (0xA0, 0xD7FF), 32 | (0xE000, 0xF8FF), 33 | (0xF900, 0xFDCF), 34 | (0xFDF0, 0xFFEF), 35 | (0x10000, 0x1FFFD), 36 | (0x20000, 0x2FFFD), 37 | (0x30000, 0x3FFFD), 38 | (0x40000, 0x4FFFD), 39 | (0x50000, 0x5FFFD), 40 | (0x60000, 0x6FFFD), 41 | (0x70000, 0x7FFFD), 42 | (0x80000, 0x8FFFD), 43 | (0x90000, 0x9FFFD), 44 | (0xA0000, 0xAFFFD), 45 | (0xB0000, 0xBFFFD), 46 | (0xC0000, 0xCFFFD), 47 | (0xD0000, 0xDFFFD), 48 | (0xE1000, 0xEFFFD), 49 | (0xF0000, 0xFFFFD), 50 | (0x100000, 0x10FFFD), 51 | ] 52 | 53 | def encode(c): 54 | retval = c 55 | i = ord(c) 56 | for low, high in escape_range: 57 | if i < low: 58 | break 59 | if i >= low and i <= high: 60 | retval = "".join(["%%%2X" % ord(o) for o in c.encode('utf-8')]) 61 | break 62 | return retval 63 | 64 | 65 | def iri2uri(uri): 66 | """Convert an IRI to a URI. Note that IRIs must be 67 | passed in a unicode strings. That is, do not utf-8 encode 68 | the IRI before passing it into the function.""" 69 | if isinstance(uri ,unicode): 70 | (scheme, authority, path, query, fragment) = urlparse.urlsplit(uri) 71 | authority = authority.encode('idna') 72 | # For each character in 'ucschar' or 'iprivate' 73 | # 1. encode as utf-8 74 | # 2. then %-encode each octet of that utf-8 75 | uri = urlparse.urlunsplit((scheme, authority, path, query, fragment)) 76 | uri = "".join([encode(c) for c in uri]) 77 | return uri 78 | 79 | if __name__ == "__main__": 80 | import unittest 81 | 82 | class Test(unittest.TestCase): 83 | 84 | def test_uris(self): 85 | """Test that URIs are invariant under the transformation.""" 86 | invariant = [ 87 | u"ftp://ftp.is.co.za/rfc/rfc1808.txt", 88 | u"http://www.ietf.org/rfc/rfc2396.txt", 89 | u"ldap://[2001:db8::7]/c=GB?objectClass?one", 90 | u"mailto:John.Doe@example.com", 91 | u"news:comp.infosystems.www.servers.unix", 92 | u"tel:+1-816-555-1212", 93 | u"telnet://192.0.2.16:80/", 94 | u"urn:oasis:names:specification:docbook:dtd:xml:4.1.2" ] 95 | for uri in invariant: 96 | self.assertEqual(uri, iri2uri(uri)) 97 | 98 | def test_iri(self): 99 | """ Test that the right type of escaping is done for each part of the URI.""" 100 | self.assertEqual("http://xn--o3h.com/%E2%98%84", iri2uri(u"http://\N{COMET}.com/\N{COMET}")) 101 | self.assertEqual("http://bitworking.org/?fred=%E2%98%84", iri2uri(u"http://bitworking.org/?fred=\N{COMET}")) 102 | self.assertEqual("http://bitworking.org/#%E2%98%84", iri2uri(u"http://bitworking.org/#\N{COMET}")) 103 | self.assertEqual("#%E2%98%84", iri2uri(u"#\N{COMET}")) 104 | self.assertEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri(u"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}")) 105 | self.assertEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri(iri2uri(u"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}"))) 106 | self.assertNotEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri(u"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}".encode('utf-8'))) 107 | 108 | unittest.main() 109 | 110 | 111 | -------------------------------------------------------------------------------- /api-client-python/localserver.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | This file allows users to run the python client without using app engine. 17 | """ 18 | from paste import httpserver 19 | from paste.cascade import Cascade 20 | from webob.static import DirectoryApp 21 | from main import web_app 22 | 23 | def main(): 24 | static_app = DirectoryApp(".", index_page=None) 25 | 26 | # Create a cascade that looks for static files first, then tries the webapp 27 | app = Cascade([static_app, web_app]) 28 | httpserver.serve(app, host='127.0.0.1', port='8080') 29 | 30 | if __name__ == '__main__': 31 | main() -------------------------------------------------------------------------------- /api-client-python/main.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 45 | 46 | 47 | 80 | 81 | 141 | 142 | 143 |
144 |
145 | 148 | 149 | 150 |
    151 | 152 | 153 |
      154 | 155 | 156 | 166 | 167 | 168 | 169 |
      170 | 171 |
      172 |
      173 |

      Add a read group set or call set to get started.

      174 |
      175 |
      176 | 177 |
      178 |
      179 | 180 | 181 | -------------------------------------------------------------------------------- /api-client-python/static/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | /* html styles */ 19 | #sequences { 20 | white-space: nowrap; 21 | overflow: auto; 22 | } 23 | 24 | #sequences .sequence { 25 | border: 1px solid #e3e3e3; 26 | border-radius: 4px; 27 | height: 80px; 28 | width: 200px; 29 | display: inline-block; 30 | margin: .5em; 31 | padding: 4px; 32 | cursor: pointer; 33 | } 34 | 35 | #sequences .sequence:hover { 36 | background-color: #f5f5f5; 37 | } 38 | 39 | #sequences .sequence.active { 40 | border-color: #357ebd; 41 | background-color: #428bca; 42 | color: white; 43 | } 44 | 45 | #sequences .sequence img { 46 | max-height: 100%; 47 | margin-right: .5em; 48 | } 49 | 50 | #sequences .sequence .title { 51 | white-space: normal; 52 | word-wrap: break-word; 53 | display: inline-block; 54 | } 55 | 56 | #sequences .sequence .summary { 57 | color: #999; 58 | } 59 | 60 | #sequences .sequence.active .summary { 61 | color: white; 62 | } 63 | 64 | #sequences .sequence .badge { 65 | background-color: #ddd; 66 | } 67 | 68 | #sequences .sequence.active .badge { 69 | background-color: white; 70 | color: #428bca; 71 | } 72 | 73 | .infoDiv h4 { 74 | word-wrap: break-word; 75 | } 76 | 77 | #setSearch .modal-body { 78 | padding-bottom: 0; 79 | } 80 | 81 | #setSearch .results { 82 | padding-top: .75em; 83 | } 84 | 85 | .paginationContainer { 86 | text-align: center; 87 | } 88 | 89 | .paginationContainer ul { 90 | margin: 0; 91 | } 92 | 93 | #jumpResults { 94 | margin: 20px 0 0 0; 95 | } 96 | 97 | #jumpResults .title { 98 | font-size: 1.1em; 99 | font-weight: 500; 100 | } 101 | 102 | .alert { 103 | z-index: 10000; 104 | position: fixed; 105 | top: 10px; 106 | left: 50%; 107 | } 108 | 109 | /* svg styles */ 110 | svg { 111 | margin-top: 20px; 112 | width: 100%; 113 | height: 800px; /* Height is updated in readgraph.js */ 114 | display: none; 115 | } 116 | .axis line, 117 | .axis path { 118 | fill: none; 119 | stroke: #000; 120 | shape-rendering: crispEdges; 121 | } 122 | .hoverline { 123 | stroke: #999; 124 | } 125 | .hovertext { 126 | fill: #999; 127 | } 128 | 129 | .positionIndicator.background { 130 | fill: red; 131 | fill-opacity: .1; 132 | } 133 | 134 | .positionIndicator a { 135 | fill: #428bca; 136 | } 137 | 138 | .zoomGroup rect { 139 | fill: white; 140 | } 141 | 142 | .read .outline { 143 | stroke: gray; 144 | fill: white; 145 | } 146 | .read.selected .outline { 147 | stroke: black; 148 | fill: #ccc 149 | } 150 | .letter { 151 | text-anchor: middle; 152 | } 153 | 154 | .variant .outline { 155 | stroke: black; 156 | } 157 | 158 | .coverageSummary, .coverage { 159 | fill: none; 160 | stroke: black; 161 | } 162 | 163 | #circleGraph path { 164 | fill: white; 165 | stroke: black; 166 | } 167 | 168 | #circleGraph path.selected { 169 | fill: gray; 170 | } -------------------------------------------------------------------------------- /api-client-python/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /api-client-python/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /api-client-python/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /api-client-python/static/img/chr1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr1.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr10.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr11.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr12.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr13.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr14.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr15.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr16.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr17.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr18.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr19.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr2.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr20.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr21.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr22.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr3.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr4.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr5.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr6.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr7.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr8.png -------------------------------------------------------------------------------- /api-client-python/static/img/chr9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chr9.png -------------------------------------------------------------------------------- /api-client-python/static/img/chrX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chrX.png -------------------------------------------------------------------------------- /api-client-python/static/img/chrY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/chrY.png -------------------------------------------------------------------------------- /api-client-python/static/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/spinner.gif -------------------------------------------------------------------------------- /api-client-python/static/img/zoom-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/zoom-bar.png -------------------------------------------------------------------------------- /api-client-python/static/img/zoom-level.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/zoom-level.png -------------------------------------------------------------------------------- /api-client-python/static/img/zoom-minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/zoom-minus.png -------------------------------------------------------------------------------- /api-client-python/static/img/zoom-plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/api-client-python/static/img/zoom-plus.png -------------------------------------------------------------------------------- /api-client-python/static/js/jquery.bootpag.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | bootpag - jQuery plugin for dynamic pagination 4 | 5 | Copyright (c) 2013 botmonster@7items.com 6 | 7 | Licensed under the MIT license: 8 | http://www.opensource.org/licenses/mit-license.php 9 | 10 | Project home: 11 | http://botmonster.com/jquery-bootpag/ 12 | 13 | Version: 1.0.5 14 | 15 | */ 16 | (function(f){f.fn.bootpag=function(n){function j(e,b){var c,d=0==a.maxVisible?1:a.maxVisible,m=1==a.maxVisible?0:1,l=Math.floor((b-1)/d)*d,g=e.find("li");a.page=b=0>b?0:b>a.total?a.total:b;g.removeClass("disabled");c=1>b-1?1:a.leaps&&b-1>=a.maxVisible?Math.floor((b-1)/d)*d:b-1;g.first().toggleClass("disabled",1===b).attr("data-lp",c).find("a").attr("href",h(c));m=1==a.maxVisible?0:1;c=b+1>a.total?a.total:a.leaps&&b+1=a.total)return this;!f.isNumeric(a.maxVisible)&&!a.maxVisible&&(a.maxVisible=a.total);k.data("settings",a);return this.each(function(){var e,b,c=f(this),d=['
        '];a.prev&&d.push('");for(b=1;b<=Math.min(a.total,a.maxVisible);b++)d.push('
      • '+b+"
      • ");a.next&&(b=a.leaps&&a.total>a.maxVisible?Math.min(a.maxVisible+ 19 | 1,a.total):2,d.push('"));d.push("
      ");c.find("ul.bootpag").remove();c.append(d.join(""));e=c.find("ul.bootpag");c.find("li").click(function(){var a=f(this);a.hasClass("disabled")||(a=parseInt(a.attr("data-lp"),10),j(e,a),k.trigger("page",a))});j(e,a.page)})}})(jQuery,window); 20 | -------------------------------------------------------------------------------- /api-client-python/static/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | "use strict"; 17 | 18 | var CALLSET_TYPE = "CALLSET"; 19 | var READSET_TYPE = "READSET"; 20 | 21 | function toggleUi(clazz, link) { 22 | $(".toggleable").hide(); 23 | $("." + clazz).show(); 24 | 25 | $('#mainNav li').removeClass('active'); 26 | $(link).parent().addClass('active'); 27 | } 28 | 29 | function showError(message) { 30 | showAlert(message, 'danger'); 31 | } 32 | 33 | function showMessage(message) { 34 | showAlert(message, 'info'); 35 | } 36 | 37 | function showAlert(message, type) { 38 | var alert = $('
      ') 39 | .addClass('alert-' + type) 40 | .text(message).appendTo($("body")); 41 | closeButton().attr('data-dismiss', 'alert').appendTo(alert); 42 | alert.css('margin-left', -1 * alert.width()/2); 43 | 44 | setTimeout(function() { 45 | alert.alert('close') 46 | }, type == 'danger' ? 5000 : 3000); 47 | } 48 | 49 | function assert(condition) { 50 | if (!condition) { 51 | console.error('assert failed'); 52 | } 53 | } 54 | 55 | function closeButton() { 56 | return $(''); 57 | } 58 | 59 | function getBackendName(backend) { 60 | return {GOOGLE : 'Google', LOCAL: 'Local'}[backend] || backend; 61 | } 62 | 63 | var loadedSetData = {}; 64 | function loadSet(readsetBackend, readsetIds, callsetBackends, callsetIds, 65 | opt_location, setType, id, backend) { 66 | if (_.has(loadedSetData, id)) { 67 | return false; 68 | } 69 | 70 | if (!backend) { 71 | showError('Backend for ' + id + ' isn\'t specified. ' + 72 | 'The URL hash is malformed.'); 73 | return; 74 | } 75 | 76 | showMessage('Loading data'); 77 | 78 | $.getJSON('/api/sets', {backend: backend, setType: setType, setId: id}) 79 | .done(function(res) { 80 | var sequenceData = _.sortBy(res.references, 81 | function(ref) { return parseInt(ref.name); }); 82 | loadedSetData[id] = {id: id, name: res.name, type: setType, 83 | backend: backend, sequences: sequenceData}; 84 | updateSets(readsetBackend, readsetIds, callsetBackends, callsetIds, 85 | opt_location); 86 | }); 87 | return true; 88 | } 89 | 90 | function updateSets(readsetBackend, readsetIds, callsetBackends, callsetIds, 91 | opt_location) { 92 | // Load missing readsets 93 | for (var i = 0; i < readsetIds.length; i++) { 94 | if (loadSet(readsetBackend, readsetIds, callsetBackends, callsetIds, 95 | opt_location, READSET_TYPE, readsetIds[i], readsetBackend)) { 96 | // Wait for the set to callback 97 | return; 98 | } 99 | } 100 | 101 | // Load missing callsets 102 | for (var j = 0; j < callsetIds.length; j++) { 103 | if (loadSet(readsetBackend, readsetIds, callsetBackends, callsetIds, 104 | opt_location, CALLSET_TYPE, callsetIds[j], callsetBackends[j])) { 105 | // Wait for the set to callback 106 | return; 107 | } 108 | } 109 | 110 | updateListItems(READSET_TYPE, readsetIds, loadedSetData); 111 | updateListItems(CALLSET_TYPE, callsetIds, loadedSetData); 112 | 113 | // Update readgraph with new sets 114 | var setData = _.filter(loadedSetData, function(data) { 115 | return (_.contains(readsetIds, data.id) && data.type == READSET_TYPE) || 116 | (_.contains(callsetIds, data.id) && data.type == CALLSET_TYPE); 117 | }); 118 | 119 | readgraph.updateSets(setData); 120 | if (setData.length > 0 && opt_location) { 121 | readgraph.jumpGraph(opt_location); 122 | } 123 | } 124 | 125 | function updateListItems(setType, ids, loadedSetData) { 126 | $('#' + setType + 'Title').toggle(ids.length > 0); 127 | var setList = $('#active' + setType).empty(); 128 | 129 | $.each(ids, function(i, id) { 130 | var setData = loadedSetData[id]; 131 | var name = setData.name; 132 | 133 | var li = $('
    • ', {'id': setType + '-' + id, 'class': 'list-group-item'}) 134 | .appendTo(setList); 135 | 136 | closeButton().appendTo(li).click(function() { 137 | removeSet(id, setType); 138 | return false; 139 | }); 140 | 141 | var displayName = getBackendName(setData.backend) + ": " + name; 142 | $('
      ', {'class': 'setName'}).text(displayName).appendTo(li); 143 | }); 144 | } 145 | 146 | function searchSets(button) { 147 | if (button) { 148 | button = $(button); 149 | button.button('loading'); 150 | } 151 | 152 | var backend = $('#backend').val(); 153 | var datasetSelector = $('#datasetId' + backend); 154 | var datasetId = datasetSelector.val(); 155 | 156 | searchSetsOfType(button, READSET_TYPE, backend, datasetId); 157 | searchSetsOfType(button, CALLSET_TYPE, backend, datasetId); 158 | } 159 | 160 | function setSearchTab(setType) { 161 | $('.tab-pane').hide(); 162 | $('.nav-tabs li').removeClass("active"); 163 | $('#' + setType + 'Tab').addClass('active'); 164 | $('#searchPane' + setType).show(); 165 | } 166 | 167 | function searchSetsOfType(button, setType, backend, datasetId) { 168 | var tabPane = $('#searchPane' + setType); 169 | var div = tabPane.find('.results') 170 | .html(''); 171 | 172 | function getItemsOnPage(page) { 173 | return div.find('.list-group-item[page=' + page + ']'); 174 | } 175 | 176 | var setsPerPage = 10; 177 | $.getJSON('/api/sets', {'backend': backend, 'datasetId': datasetId, 178 | 'setType': setType, 'name': $('#setName').val()}) 179 | .done(function(res) { 180 | div.empty(); 181 | 182 | var pagination = tabPane.find('.paginationContainer'); 183 | pagination.hide(); 184 | 185 | var sets = res.readGroupSets || res.callSets; 186 | if (!sets) { 187 | div.html('No data found'); 188 | return; 189 | } 190 | 191 | var totalPages = Math.ceil(sets.length / setsPerPage); 192 | 193 | $.each(sets, function(i, data) { 194 | var page = Math.floor(i / setsPerPage) + 1; 195 | $('', {'href': '#', 'class': 'list-group-item', 'page': page}) 196 | .text(data.name).appendTo(div).click(function() { 197 | switchToSet(backend, setType, data.id); 198 | return false; 199 | }).hide(); 200 | }); 201 | getItemsOnPage(1).show(); 202 | 203 | if (totalPages > 1) { 204 | pagination.show(); 205 | pagination.bootpag({ 206 | page: 1, 207 | total: totalPages, 208 | maxVisible: 10 209 | }).on("page", function(event, newPage) { 210 | div.find('.list-group-item').hide(); 211 | getItemsOnPage(newPage).show(); 212 | }); 213 | } 214 | 215 | }).always(function() { 216 | button && button.button('reset'); 217 | }); 218 | } 219 | 220 | 221 | // Hash functions 222 | function setAnchor(map) { 223 | window.location.hash = $.param(map, true); 224 | } 225 | 226 | var arrayKeys = ['backend', 'readsetId', 'cBackend', 'callsetId']; 227 | function getAnchorMap() { 228 | var hashParts = window.location.hash.substring(1).split('&'); 229 | var map = {}; 230 | for (var i = 0; i < hashParts.length; i++) { 231 | var option = decodeURIComponent(hashParts[i]).split('='); 232 | var key = option[0]; 233 | var value = option[1]; 234 | 235 | if (!_.contains(arrayKeys, key)) { 236 | map[key] = value; 237 | } else if (map[key]) { 238 | map[key].push(value); 239 | } else { 240 | map[key] = [value]; 241 | } 242 | } 243 | 244 | return map; 245 | } 246 | 247 | function removeSet(id, setType) { 248 | var state = getAnchorMap(); 249 | var key = setType == READSET_TYPE ? 'readsetId' : 'callsetId'; 250 | var backendKey = setType == READSET_TYPE ? 'backend' : 'cBackend'; 251 | var setIndex = _.indexOf(state[key], id); 252 | state[key].splice(setIndex, 1); 253 | state[backendKey].splice(setIndex, 1); 254 | if (state[key].length == 0) { 255 | delete state[key]; 256 | } 257 | if (state[backendKey].length == 0) { 258 | delete state[backendKey]; 259 | } 260 | setAnchor(state); 261 | } 262 | 263 | function switchToSet(backend, setType, id) { 264 | var state = getAnchorMap(); 265 | var key = setType == READSET_TYPE ? 'readsetId' : 'callsetId'; 266 | 267 | if (setType == READSET_TYPE) { 268 | // TODO: Support multiple readsets at once 269 | state[key] = [id]; 270 | state.backend = [backend]; 271 | } else { 272 | state[key] = (state[key] || []); 273 | state[key].push(id); 274 | 275 | state.cBackend = (state.cBackend || []); 276 | state.cBackend.push(backend); 277 | } 278 | 279 | setAnchor(state); 280 | $('#setSearch').modal('hide'); 281 | } 282 | 283 | function switchToLocation(location) { 284 | var state = _.extend(getAnchorMap(), {'location': location}); 285 | setAnchor(state); 286 | } 287 | 288 | function updateUserLocation(location) { 289 | switchToLocation(readgraph.jumpGraph(location)); 290 | } 291 | 292 | function handleHash() { 293 | var state = getAnchorMap(); 294 | var readsetBackend = (state.backend || [])[0]; 295 | if (readsetBackend) { 296 | $("#backend").val(readsetBackend); // TODO: Get rid of this? 297 | } 298 | 299 | updateSets( 300 | readsetBackend, 301 | (state.readsetId || []).slice(0, 1), 302 | state.cBackend || [], 303 | state.callsetId || [], 304 | state.location); 305 | if (state.location) { 306 | // Strip off the chromosome prefix 307 | var colonIndex = state.location.indexOf(":"); 308 | var location = state.location.substring(colonIndex + 1); 309 | $("#readsetPosition").val(location); 310 | } 311 | } 312 | 313 | 314 | // Show the about popup when the page loads the first time, read the hash, 315 | // and prep the initial set search 316 | $(document).ready(function() { 317 | if (!sessionStorage.getItem('about-shown')) { 318 | sessionStorage.setItem('about-shown', true); 319 | $('#about').modal('show'); 320 | } 321 | 322 | $(document).ajaxError(function(e, xhr) { 323 | showError('Sorry, the api request failed for some reason. ' + 324 | '(' + xhr.responseText + ')'); 325 | }); 326 | 327 | $(window).on('hashchange', handleHash); 328 | handleHash(); 329 | 330 | $('#backend').change(function() { 331 | $('.datasetSelector').hide(); 332 | $('#datasetId' + $(this).val()).show(); 333 | }).change(); 334 | 335 | // TODO: Simplify with general searchTab classes 336 | $("#READSETTab").click(function() { 337 | setSearchTab(READSET_TYPE); 338 | return false; 339 | }); 340 | $("#CALLSETTab").click(function() { 341 | if ($(this).hasClass("disabled")) { 342 | return false; 343 | } 344 | setSearchTab(CALLSET_TYPE); 345 | return false; 346 | }); 347 | 348 | searchSets(); 349 | }); -------------------------------------------------------------------------------- /api-client-python/static/js/readcache.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | "use strict"; 17 | 18 | /* 19 | * Return whether two ranges overlap at all. 20 | */ 21 | function overlaps(start1, end1, start2, end2) { 22 | return start1 < end2 && end1 > start2; 23 | }; 24 | 25 | /* 26 | * A data structure for keeping track of all the reads we have loaded. 27 | * The objects in the cache have already been processed, and so must have 28 | * their computed fields like end and readPieces (rather than the raw fields 29 | * like alignedSequence). 30 | */ 31 | var readCache = new function() { 32 | /* 33 | * Whether we're caching base data. All reads stored into the cache 34 | * should have the 'alignedSequence' property if and only if this is true. 35 | * When this changes from true to false, all cached base data is cleared. 36 | * But when this changes from false to true, reads without base data are 37 | * preserved until they can be updated to include the base data. 38 | */ 39 | var wantBases = false; 40 | this.__defineGetter__("wantBases", function() { return wantBases; }); 41 | 42 | /* 43 | * The range [start,end) of sequence positions for which we are caching reads. 44 | * There should never be any reads inside the cache that lie entirely outside 45 | * this range. 46 | */ 47 | var start = 0; 48 | var end = 0; 49 | this.__defineGetter__("start", function() { return start; }); 50 | this.__defineGetter__("end", function() { return end; }); 51 | 52 | // A map from read ID to read object. 53 | var readsById = {}; 54 | 55 | // An array of RBTrees, one per track, containing all the reads assigned to 56 | // that track by position. 57 | var yTracks = []; 58 | 59 | /* 60 | * Returns all the reads in the cache. 61 | */ 62 | this.getReads = function() { 63 | return d3.values(readsById); 64 | }; 65 | 66 | /* 67 | * Clear the entire cache. 68 | */ 69 | this.clear = function() { 70 | wantBases = false; 71 | start = 0; 72 | end = 0; 73 | readsById = {}; 74 | yTracks = []; 75 | }; 76 | 77 | // Remove a specific read from the cache. 78 | function removeRead(read) { 79 | var removed = yTracks[read.yOrder].remove(read); 80 | assert(removed); 81 | assert(readsById[read.id] === read); 82 | delete readsById[read.id]; 83 | }; 84 | 85 | /* 86 | * Reset the cache range, clearing elements / base data that are no longer 87 | * necessary. 88 | */ 89 | this.setRange = function(newStart, newEnd, newBases) { 90 | // Find all reads outside the new range and remove them. 91 | // Ideally our RBTree implementation would just support 92 | // efficiently removing ranges of nodes, or removing given an iterator. 93 | for (var y = 0; y < yTracks.length; y++) { 94 | var tree = yTracks[y]; 95 | 96 | // Remove nodes from the front as long as they're outside our range. 97 | for(var read = tree.min(); read && read.end < newStart; read = tree.min()) { 98 | assert(read.yOrder == y); 99 | removeRead(read); 100 | } 101 | 102 | // Remove nodes from the end as long as they're outside our range. 103 | for(var read = tree.max(); read && read.position >= newEnd; read = tree.max()) { 104 | assert(read.yOrder == y); 105 | removeRead(read); 106 | } 107 | } 108 | 109 | // Discard stored bases if we don't want them anymore. 110 | if (wantBases && !newBases) { 111 | $.each(readsById, function(id, read) { 112 | read.readPieces = []; 113 | }); 114 | } 115 | 116 | // Discard any cached reads that are now entirely outside our desired window. 117 | $.each(readsById, function(id, read) { 118 | if (!overlaps(read.position, read.end, newStart, newEnd)) { 119 | delete readsById[id]; 120 | } else if (!newBases) { 121 | read.readPieces = []; 122 | } 123 | }); 124 | 125 | start = newStart; 126 | end = newEnd; 127 | wantBases = newBases; 128 | }; 129 | 130 | /* 131 | * Return whether the cache already contains a read with the specified 132 | * id and base status. 133 | */ 134 | this.hasRead = function(id, bases) { 135 | var existingRead = readsById[id]; 136 | if (existingRead && bases == ('readPieces' in existingRead)) { 137 | return true; 138 | } 139 | return false; 140 | }; 141 | 142 | // Comparer function for the RBTrees 143 | function readOverlapComparer(r1, r2) { 144 | if (overlaps(r1.position, r1.end, r2.position, r2.end)) { 145 | return 0; // reads that overlap are considered "equal" 146 | } 147 | // Since we know that no two reads in the tree overlap, we can sort 148 | // just by the start position. 149 | return r1.position - r2.position; 150 | } 151 | 152 | /* 153 | * Adds the supplied read to the cache if it's still relevant, assigning a 154 | * free yOrder property to thr read. 155 | * If a read with this ID already exists, updates the read (eg. to add 156 | * or remove base data) without changing the yOrder. 157 | */ 158 | this.addOrUpdateRead = function(read) { 159 | // The read should have already been processed. 160 | assert('end' in read); 161 | assert('readPieces' in read); 162 | assert(!('alignedSequence' in read)); 163 | 164 | // If we don't actually want this read anymore, do nothing. 165 | if (!overlaps(read.position, read.end, start, end) 166 | || (read.readPieces.length > 0) != wantBases) { 167 | return; 168 | } 169 | 170 | var existingRead = readsById[read.id]; 171 | if (existingRead) { 172 | read.yOrder = existingRead.yOrder; 173 | var removed = yTracks[read.yOrder].remove(existingRead); 174 | assert(removed); 175 | var inserted = yTracks[read.yOrder].insert(read); 176 | assert(inserted); 177 | } else { 178 | // Find the lowest available track for this read. 179 | assert(!('yOrder' in read)); 180 | for(read.yOrder = 0;; read.yOrder++) { 181 | if (read.yOrder < yTracks.length) { 182 | if (yTracks[read.yOrder].insert(read)) { 183 | // Successfully inserted into this track. 184 | break; 185 | } 186 | } else { 187 | // Need a new track. 188 | var newTree = new RBTree(readOverlapComparer); 189 | yTracks[read.yOrder] = newTree; 190 | var inserted = newTree.insert(read); 191 | assert(inserted); 192 | break; 193 | } 194 | } 195 | } 196 | readsById[read.id] = read; 197 | }; 198 | }; 199 | 200 | -------------------------------------------------------------------------------- /api-client-python/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /gatk-tools-java/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | .classpath 3 | .project 4 | .jar_tmp 5 | classes 6 | dist 7 | gatk-tools-java.version.properties 8 | -------------------------------------------------------------------------------- /gatk-tools-java/CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | How to contribute 2 | =================================== 3 | 4 | First of all, thank you for contributing! 5 | 6 | The mailing list 7 | ---------------- 8 | 9 | For general questions or if you are having trouble getting started, try the 10 | `Google Genomics Discuss mailing list `_. 11 | It's a good way to sync up with other people who use googlegenomics including the core developers. You can subscribe 12 | by sending an email to ``google-genomics-discuss+subscribe@googlegroups.com`` or just post using 13 | the `web forum page `_. 14 | 15 | 16 | Submitting issues 17 | ----------------- 18 | 19 | If you are encountering a bug in the code or have a feature request in mind - file away! 20 | 21 | 22 | Submitting a pull request 23 | ------------------------- 24 | 25 | If you are ready to contribute code, Github provides a nice `overview on how to create a pull request 26 | `_. 27 | 28 | Some general rules to follow: 29 | 30 | * Do your work in `a fork `_ of this repo. 31 | * Create a branch for each update that you're working on. 32 | These branches are often called "feature" or "topic" branches. Any changes 33 | that you push to your feature branch will automatically be shown in the pull request. 34 | * Keep your pull requests as small as possible. Large pull requests are hard to review. 35 | Try to break up your changes into self-contained and incremental pull requests. 36 | * The first line of commit messages should be a short (<80 character) summary, 37 | followed by an empty line and then any details that you want to share about the commit. 38 | * Please try to follow the existing syntax style 39 | 40 | When you submit or change your pull request, the Travis build system will automatically run tests. 41 | If your pull request fails to pass tests, review the test log, make changes and 42 | then push them to your feature branch to be tested again. 43 | 44 | 45 | Contributor License Agreements 46 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 47 | 48 | All pull requests are welcome. Before we can submit them though, there is a legal hurdle we have to jump. 49 | You'll need to fill out either the individual or corporate Contributor License Agreement 50 | (CLA). 51 | 52 | * If you are an individual writing original source code and you're sure you 53 | own the intellectual property, then you'll need to sign an `individual CLA 54 | `_. 55 | * If you work for a company that wants to allow you to contribute your work, 56 | then you'll need to sign a `corporate CLA 57 | `_. 58 | 59 | Follow either of the two links above to access the appropriate CLA and 60 | instructions for how to sign and return it. Once we receive it, we'll be able to 61 | accept your pull requests. 62 | -------------------------------------------------------------------------------- /gatk-tools-java/README.md: -------------------------------------------------------------------------------- 1 | 2 | gatk-tools-java 3 | =============== 4 | Tools for using Picard and GATK with Genomics API. 5 | 6 | - Common classes for getting Reads from GA4GH Genomics API and 7 | exposing them as SAMRecord "Iterable" resource. 8 | 9 | - Implementation of a custom reader that can be plugged into Picard tools 10 | to handle reading of the input data specified via a url and coming from GA4GH API. 11 | 12 | - A set of shell scripts (src/main/scripts) that demonstrate how to run Picard 13 | tools with Ga4GH custom reader. 14 | 15 | - Requires htsjdk version 1.128 and greater and Picard latest version (past this commit https://github.com/iliat/picard/commit/ebe987313d799d58b0673351b95d3ca91fed82bf). 16 | 17 | - You can download Picard from: http://broadinstitute.github.io/picard/ and 18 | build it according to the instructions. 19 | 20 | Build: 21 | To build with ant: 22 | ant gatk-tools-java-jar. 23 | 24 | Note that examples below assume you have built with ant, 25 | it produces dist/gatk-tools-java-1.0.jar 26 | The following examples assume you have picard folder side by side with gatk-tools-java. 27 | 28 | The typical command line would look like: 29 | 30 | java -jar \ 31 | -Dsamjdk.custom_reader=https://www.googleapis.com/genomics, \ 32 | -Dga4gh.client_secrets= \ 33 | dist/picard.jar \ 34 | INPUT= 35 | 36 | E.g 37 | 38 | java -jar \ 39 | -Dsamjdk.custom_reader=https://www.googleapis.com/genomics,com.google.cloud.genomics.gatk.htsjdk.GA4GHReaderFactory,\ 40 | `pwd`/dist/gatk-tools-java-1.0.jar \ 41 | -Dga4gh.client_secrets=client_secrets.json \ 42 | ../picard/dist/picard.jar ViewSam \ 43 | INPUT=https://www.googleapis.com/genomics/v1beta2/readgroupsets/CK256frpGBD44IWHwLP22R4/ 44 | The test read group set used here is the ex1_sorted.bam that can be found in testdata/ folder. 45 | The data has been uploaded to the cloud project: https://console.developers.google.com/project/genomics-test-data/ 46 | 47 | To build with Maven: 48 | mvn compile 49 | mvn bundle:bundle. 50 | Note that Maven build produces gatk-tools-java-1.1-SNAPSHOT.jar. 51 | 52 | - For Picard tools that have not yet been instrumented to work with a custom reader, 53 | you can use Ga4GHPicardRunner. 54 | It is a wrapper around Picard tools that allows for INPUTS into 55 | Picard tools to be ga4gh:// urls by consuming the data via the API and using pipes 56 | to send it to Picard tool. 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /gatk-tools-java/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 57 | 58 | 59 | 60 | 61 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /gatk-tools-java/lib/genomics-tools-client-java-v1beta.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/gatk-tools-java/lib/genomics-tools-client-java-v1beta.jar -------------------------------------------------------------------------------- /gatk-tools-java/lib/google-genomics-utils-v1beta2-0.20-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/gatk-tools-java/lib/google-genomics-utils-v1beta2-0.20-SNAPSHOT.jar -------------------------------------------------------------------------------- /gatk-tools-java/lib/guava-18.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/gatk-tools-java/lib/guava-18.0.jar -------------------------------------------------------------------------------- /gatk-tools-java/lib/htsjdk-1.129.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/gatk-tools-java/lib/htsjdk-1.129.jar -------------------------------------------------------------------------------- /gatk-tools-java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.google.cloud.genomics 4 | gatk-tools-java 5 | jar 6 | 7 | Google Genomics Utils for GATK 8 | Common Java files for Google Genomics integrations with GATK/Picard/HTSJDK 9 | https://github.com/googlegenomics/gatk-tools-java 10 | 1.1-SNAPSHOT 11 | 12 | 13 | Google 14 | http://www.google.com/ 15 | 16 | 17 | 18 | 19 | The Apache Software License, Version 2.0 20 | http://www.apache.org/licenses/LICENSE-2.0.txt 21 | repo 22 | 23 | 24 | 25 | 26 | 27 | iliat 28 | Ilia Tulchinsky 29 | Google 30 | http://www.google.com 31 | -5 32 | 33 | 34 | dloy 35 | Google 36 | http://www.google.com 37 | -7 38 | 39 | 40 | 41 | 42 | scm:git:git@github.com:googlegenomics/gatk-tools-java.git 43 | scm:git:git@github.com:googlegenomics/gatk-tools-java.git 44 | git@github.com:googlegenomics/gatk-tools-java.git 45 | HEAD 46 | 47 | 48 | 49 | 50 | ossrh 51 | https://oss.sonatype.org/content/repositories/snapshots/ 52 | 53 | 54 | ossrh 55 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 56 | 57 | 58 | 59 | 60 | 61 | 62 | com.google.api-client 63 | google-api-client 64 | ${google.api.version} 65 | 66 | 68 | 69 | com.google.guava 70 | guava-jdk5 71 | 72 | 73 | 74 | 75 | com.google.oauth-client 76 | google-oauth-client 77 | ${google.api.version} 78 | 79 | 80 | com.google.oauth-client 81 | google-oauth-client-jetty 82 | ${google.api.version} 83 | 84 | 85 | com.google.oauth-client 86 | google-oauth-client-java6 87 | ${google.api.version} 88 | 89 | 90 | com.google.http-client 91 | google-http-client 92 | ${google.api.version} 93 | 94 | 95 | com.google.http-client 96 | google-http-client-jackson2 97 | ${google.api.version} 98 | 99 | 100 | com.google.apis 101 | google-api-services-genomics 102 | v1beta2-rev9-1.19.0 103 | 104 | 106 | 107 | com.google.guava 108 | guava-jdk5 109 | 110 | 111 | 112 | 113 | com.google.cloud.genomics 114 | google-genomics-utils 115 | v1beta2-0.19 116 | 117 | 119 | 120 | com.google.guava 121 | guava-jdk5 122 | 123 | 124 | 125 | 126 | com.github.samtools 127 | htsjdk 128 | 1.128 129 | 130 | 131 | org.testng 132 | testng 133 | 134 | 135 | 136 | 137 | com.google.guava 138 | guava 139 | 18.0 140 | 141 | 142 | gov.nist.math.jama 143 | gov.nist.math.jama 144 | 1.1.1 145 | 146 | 147 | org.reflections 148 | reflections 149 | 0.9.9 150 | 151 | 152 | junit 153 | junit 154 | 4.11 155 | test 156 | 157 | 158 | org.hamcrest 159 | hamcrest-all 160 | 1.3 161 | test 162 | 163 | 164 | org.mortbay.jetty 165 | jetty 166 | ${jetty.version} 167 | runtime 168 | 169 | 170 | org.mortbay.jetty 171 | jetty-util 172 | ${jetty.version} 173 | runtime 174 | 175 | 176 | javax.servlet 177 | servlet-api 178 | 2.5 179 | runtime 180 | 181 | 182 | junit 183 | junit 184 | 4.11 185 | test 186 | 187 | 188 | org.mockito 189 | mockito-core 190 | 1.10.8 191 | test 192 | 193 | 194 | com.beust 195 | jcommander 196 | ${jcommander.version} 197 | 198 | 199 | 200 | 201 | 202 | release 203 | 204 | 205 | 206 | org.apache.maven.plugins 207 | maven-gpg-plugin 208 | 1.5 209 | 210 | 211 | sign-artifacts 212 | verify 213 | 214 | sign 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | org.apache.maven.plugins 227 | maven-compiler-plugin 228 | 3.2 229 | 230 | ${java.version} 231 | ${java.version} 232 | 233 | 234 | 235 | org.apache.maven.plugins 236 | maven-release-plugin 237 | 2.5 238 | 239 | true 240 | release 241 | deploy 242 | 243 | 244 | 245 | org.eluder.coveralls 246 | coveralls-maven-plugin 247 | 2.2.0 248 | 249 | 250 | org.apache.felix 251 | maven-bundle-plugin 252 | 2.4.0 253 | true 254 | 255 | 256 | 257 | true 258 | *;scope=compile|runtime;inline=true 259 | 260 | 261 | 262 | 263 | 264 | org.apache.maven.plugins 265 | maven-dependency-plugin 266 | 2.8 267 | 268 | 269 | copy-dependencies 270 | package 271 | 272 | copy-dependencies 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 6.1.26 282 | 2.4.2 283 | 4.11 284 | 1.19.0 285 | v1beta2-rev7-1.19.0 286 | 1.10.8 287 | 1.35 288 | UTF-8 289 | UTF-8 290 | 1.7 291 | 292 | 293 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/common/GA4GHUrl.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.google.cloud.genomics.gatk.common; 17 | 18 | import java.net.URISyntaxException; 19 | import java.net.URL; 20 | 21 | /** 22 | * Represents a GA4GH reads resource as a URL in the form of 23 | * ga4gh:///readsets///[start-end], 24 | * e.g. ga4gh://www.googleapis.com/genomics/v1beta2/readgroupsets/CMvnhpKTFhD04eLE-q2yxnU/1/ 25 | */ 26 | public class GA4GHUrl { 27 | int rangeStart = 0; 28 | int rangeEnd = 0; 29 | String rootUrl = ""; 30 | String readset = ""; 31 | String sequence = ""; 32 | 33 | private static String READS_PATH_COMPONENT = "/readgroupsets/"; 34 | private static String GA4GH_SCHEMA_PREFIX = "ga4gh://"; 35 | 36 | public static boolean isGA4GHUrl(String url) { 37 | return url.toLowerCase().startsWith(GA4GH_SCHEMA_PREFIX); 38 | } 39 | 40 | public GA4GHUrl() { 41 | 42 | } 43 | 44 | public GA4GHUrl(String rootUrl, 45 | String readset, 46 | String sequence, 47 | int rangeStart, 48 | int rangeEnd) { 49 | super(); 50 | this.rootUrl = rootUrl; 51 | this.readset = readset; 52 | this.sequence = sequence; 53 | this.rangeStart = rangeStart; 54 | this.rangeEnd = rangeEnd; 55 | } 56 | 57 | public GA4GHUrl(URL input) throws URISyntaxException { 58 | this(input.toString().replace("https://", GA4GH_SCHEMA_PREFIX)); 59 | } 60 | 61 | public GA4GHUrl(String input) throws URISyntaxException { 62 | if (!isGA4GHUrl(input)) { 63 | throw new URISyntaxException(input, "Schema is not ga4gh"); 64 | } 65 | int pos = input.indexOf(READS_PATH_COMPONENT); 66 | if (pos < 0) { 67 | throw new URISyntaxException(input, "Can not find " + READS_PATH_COMPONENT 68 | + " path component"); 69 | } 70 | rootUrl = input.substring(0, pos).replace(GA4GH_SCHEMA_PREFIX, "https://"); 71 | String readsPath = input.substring(pos); 72 | String[] pathComponents = readsPath.split("/"); 73 | if (pathComponents.length < 3) { 74 | throw new URISyntaxException(input, 75 | "Expecting " + READS_PATH_COMPONENT + "readgroupset/sequence/[range], got " 76 | + readsPath); 77 | } 78 | 79 | readset = pathComponents[2]; 80 | if (pathComponents.length > 3) { 81 | sequence = pathComponents[3]; 82 | } 83 | 84 | if (pathComponents.length > 4) { 85 | String [] range = pathComponents[4].split("-"); 86 | if (range.length == 2) { 87 | rangeStart = Integer.parseInt(range[0]); 88 | rangeEnd = Integer.parseInt(range[1]); 89 | } else { 90 | throw new URISyntaxException(input, 91 | "Expecting last component to be -, got " + 92 | readsPath); 93 | } 94 | } 95 | } 96 | 97 | /** 98 | * @return the rangeStart 99 | */ 100 | public int getRangeStart() { 101 | return rangeStart; 102 | } 103 | 104 | /** 105 | * @param rangeStart the rangeStart to set 106 | */ 107 | public void setRangeStart(int rangeStart) { 108 | this.rangeStart = rangeStart; 109 | } 110 | 111 | /** 112 | * @return the rangeEnd 113 | */ 114 | public int getRangeEnd() { 115 | return rangeEnd; 116 | } 117 | 118 | /** 119 | * @param rangeEnd the rangeEnd to set 120 | */ 121 | public void setRangeEnd(int rangeEnd) { 122 | this.rangeEnd = rangeEnd; 123 | } 124 | 125 | /** 126 | * @return the rootUrl 127 | */ 128 | public String getRootUrl() { 129 | return rootUrl; 130 | } 131 | 132 | /** 133 | * @param rootUrl the rootUrl to set 134 | */ 135 | public void setRootUrl(String rootUrl) { 136 | this.rootUrl = rootUrl; 137 | } 138 | 139 | /** 140 | * @return the readset 141 | */ 142 | public String getReadset() { 143 | return readset; 144 | } 145 | 146 | /** 147 | * @param readset the readset to set 148 | */ 149 | public void setReadset(String readset) { 150 | this.readset = readset; 151 | } 152 | 153 | /** 154 | * @return the sequence 155 | */ 156 | public String getSequence() { 157 | return sequence; 158 | } 159 | 160 | /** 161 | * @param sequence the sequence to set 162 | */ 163 | public void setSequence(String sequence) { 164 | this.sequence = sequence; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/common/GenomicsApiDataSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.google.cloud.genomics.gatk.common; 17 | 18 | import com.google.api.client.extensions.java6.auth.oauth2.VerificationCodeReceiver; 19 | import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; 20 | import com.google.api.client.googleapis.extensions.java6.auth.oauth2.GooglePromptReceiver; 21 | import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; 22 | import com.google.api.client.googleapis.json.GoogleJsonResponseException; 23 | import com.google.api.client.http.HttpRequest; 24 | import com.google.api.client.http.HttpRequestInitializer; 25 | import com.google.api.client.json.jackson2.JacksonFactory; 26 | import com.google.api.services.genomics.Genomics; 27 | import com.google.api.services.genomics.model.Read; 28 | import com.google.api.services.genomics.model.ReadGroup; 29 | import com.google.api.services.genomics.model.ReadGroupSet; 30 | import com.google.api.services.genomics.model.Reference; 31 | import com.google.api.services.genomics.model.ReferenceSet; 32 | import com.google.api.services.genomics.model.SearchReadsRequest; 33 | import com.google.cloud.genomics.utils.GenomicsFactory; 34 | import com.google.cloud.genomics.utils.Paginator; 35 | import com.google.common.base.Suppliers; 36 | import com.google.common.collect.Lists; 37 | import com.google.common.collect.Maps; 38 | import com.google.common.collect.Sets; 39 | 40 | import java.io.File; 41 | import java.io.IOException; 42 | import java.security.GeneralSecurityException; 43 | import java.util.Arrays; 44 | import java.util.Map; 45 | import java.util.Set; 46 | import java.util.logging.Logger; 47 | 48 | /** 49 | * Manages Genomics Api initialization and provides Read iterator based 50 | * resources via Reads.search API invocations. 51 | */ 52 | public class GenomicsApiDataSource { 53 | private static final Logger LOG = Logger.getLogger(GenomicsApiDataSource.class.getName()); 54 | 55 | private String clientSecretsFilename; 56 | private boolean noLocalServer; 57 | private String rootUrl; 58 | 59 | /** Genomics API stub */ 60 | private Genomics api = null; 61 | 62 | public GenomicsApiDataSource(String rootUrl, 63 | String clientSecretsFilename, 64 | boolean noLocalServer) { 65 | super(); 66 | this.clientSecretsFilename = clientSecretsFilename; 67 | this.noLocalServer = noLocalServer; 68 | this.rootUrl = rootUrl; 69 | } 70 | 71 | private Genomics getApi() throws GeneralSecurityException, IOException { 72 | if (api == null) { 73 | api = initGenomicsApi(); 74 | } 75 | return api; 76 | } 77 | 78 | private Genomics initGenomicsApi() throws GeneralSecurityException, IOException { 79 | LOG.info("Initializing Genomics API for " + rootUrl); 80 | if (!clientSecretsFilename.isEmpty()) { 81 | File clientSecrets = new File(clientSecretsFilename); 82 | if (!clientSecrets.exists()) { 83 | throw new IOException( 84 | "Client secrets file " + clientSecretsFilename + " does not exist." 85 | + " Visit https://cloud.google.com/genomics/install-genomics-tools#authenticate to learn how" 86 | + " to install a client_secrets.json file. If you have installed a client_secrets.json" 87 | + " in a specific location, use --client_secrets_filename /client_secrets.json."); 88 | } 89 | LOG.info("Using client secrets file " + clientSecretsFilename); 90 | 91 | VerificationCodeReceiver receiver = noLocalServer ? 92 | new GooglePromptReceiver() : new LocalServerReceiver(); 93 | GenomicsFactory genomicsFactory = GenomicsFactory 94 | .builder("genomics_java_client") 95 | .setRootUrl(rootUrl) 96 | .setServicePath("/") 97 | .setVerificationCodeReceiver(Suppliers.ofInstance(receiver)) 98 | .build(); 99 | return genomicsFactory.fromClientSecretsFile(clientSecrets); 100 | } else { 101 | final Genomics.Builder builder = new Genomics 102 | .Builder( 103 | GoogleNetHttpTransport.newTrustedTransport(), 104 | JacksonFactory.getDefaultInstance(), 105 | new HttpRequestInitializer() { 106 | @Override public void initialize(HttpRequest httpRequest) throws IOException { 107 | httpRequest.setReadTimeout(20000); 108 | httpRequest.setConnectTimeout(20000); 109 | } 110 | }) 111 | .setApplicationName("genomics_java_client") 112 | .setRootUrl(rootUrl) 113 | .setServicePath("/"); 114 | return builder.build(); 115 | } 116 | } 117 | 118 | public ReadIteratorResource getReadsFromGenomicsApi(GA4GHUrl url) 119 | throws IOException, GeneralSecurityException { 120 | LOG.info("Getting reads from " + url); 121 | return getReadsFromGenomicsApi(url.getReadset(), 122 | url.getSequence(), url.getRangeStart(), url.getRangeEnd()); 123 | } 124 | 125 | public ReadIteratorResource getReadsFromGenomicsApi(String readsetId, 126 | String sequenceName, int sequenceStart, int sequenceEnd) 127 | throws IOException, GeneralSecurityException { 128 | LOG.info("Getting readset " + readsetId + ", sequence " + sequenceName + 129 | ", start=" + sequenceStart + ", end=" + sequenceEnd); 130 | final Genomics stub = getApi(); 131 | // TODO(iliat): implement API retries and using access key for public 132 | // datasets 133 | try { 134 | ReadGroupSet readGroupSet = stub.readgroupsets().get(readsetId).execute(); 135 | String datasetId = readGroupSet.getDatasetId(); 136 | LOG.info("Found readset " + readsetId + ", dataset " + datasetId); 137 | 138 | final Map references = getReferences(readGroupSet); 139 | final Reference reference = references.get(sequenceName); 140 | if (reference != null) { 141 | LOG.info("Reference for sequence name " + sequenceName + " is found, length=" 142 | + String.valueOf(reference.getLength())); 143 | } else { 144 | LOG.warning("Reference for sequence name " + sequenceName + " not found"); 145 | } 146 | LOG.info("Searching for reads in sequence " + sequenceName + 147 | String.valueOf(sequenceStart) + "-" + String.valueOf(sequenceEnd)); 148 | UnmappedReads unmappedReads = null; 149 | if (sequenceName.isEmpty()) { 150 | unmappedReads = getUnmappedMatesOfMappedReads(readsetId); 151 | } 152 | Paginator.Reads searchReads = Paginator.Reads.create(stub); 153 | SearchReadsRequest readRequest = new SearchReadsRequest() 154 | .setReadGroupSetIds(Arrays.asList(readsetId)) 155 | .setReferenceName(sequenceName) 156 | .setPageSize(2048); 157 | if (sequenceStart != 0) { 158 | readRequest.setStart(Long.valueOf(sequenceStart)); 159 | } 160 | if (sequenceEnd != 0) { 161 | readRequest.setEnd(Long.valueOf(sequenceEnd)); 162 | } 163 | Iterable reads = searchReads.search(readRequest); 164 | 165 | return new ReadIteratorResource(readGroupSet, 166 | Lists.newArrayList(references.values()), unmappedReads, reads); 167 | } catch (GoogleJsonResponseException ex) { 168 | LOG.warning("Genomics API call failure: " + ex.getMessage()); 169 | if (ex.getDetails() == null) { 170 | throw ex; 171 | } 172 | throw new IOException(ex.getDetails().getMessage()); 173 | } 174 | } 175 | 176 | /** 177 | * Collect a list of references mentioned in this Readgroupset and get their meta data. 178 | * @throws GeneralSecurityException 179 | * @throws IOException 180 | */ 181 | private Map getReferences(ReadGroupSet readGroupSet) 182 | throws IOException, GeneralSecurityException { 183 | Set referenceSetIds = Sets.newHashSet(); 184 | if (readGroupSet.getReferenceSetId() != null) { 185 | LOG.info("Found reference set from read group set " + 186 | readGroupSet.getReferenceSetId()); 187 | referenceSetIds.add(readGroupSet.getReferenceSetId()); 188 | } 189 | if (readGroupSet.getReadGroups() != null) { 190 | LOG.info("Found read groups"); 191 | for (ReadGroup readGroup : readGroupSet.getReadGroups()) { 192 | if (readGroup.getReferenceSetId() != null) { 193 | LOG.info("Found reference set from read group: " + 194 | readGroup.getReferenceSetId()); 195 | referenceSetIds.add(readGroup.getReferenceSetId()); 196 | } 197 | } 198 | } 199 | 200 | Map references = Maps.newHashMap(); 201 | for (String referenceSetId : referenceSetIds) { 202 | LOG.info("Getting reference set " + referenceSetId); 203 | ReferenceSet referenceSet = getApi().referencesets().get(referenceSetId).execute(); 204 | if (referenceSet == null || referenceSet.getReferenceIds() == null) { 205 | continue; 206 | } 207 | for (String referenceId : referenceSet.getReferenceIds()) { 208 | LOG.info("Getting reference " + referenceId); 209 | Reference reference = getApi().references().get(referenceId).execute(); 210 | if (reference.getName() != null) { 211 | references.put(reference.getName(), reference); 212 | LOG.info("Adding reference " + reference.getName()); 213 | } 214 | } 215 | } 216 | return references; 217 | } 218 | 219 | private UnmappedReads getUnmappedMatesOfMappedReads(String readsetId) 220 | throws GeneralSecurityException, IOException { 221 | LOG.info("Collecting unmapped mates of mapped reads for injection"); 222 | final Paginator.Reads searchUnmappedReads = Paginator.Reads.create(getApi()); 223 | final SearchReadsRequest unmappedReadRequest = new SearchReadsRequest() 224 | .setReadGroupSetIds(Arrays.asList(readsetId)) 225 | .setReferenceName("*"); 226 | final Iterable unmappedReadsIterable = searchUnmappedReads.search(unmappedReadRequest); 227 | final UnmappedReads unmappedReads = new UnmappedReads(); 228 | for (Read read : unmappedReadsIterable) { 229 | unmappedReads.maybeAddRead(read); 230 | } 231 | LOG.info("Finished collecting unmapped mates of mapped reads: " + 232 | unmappedReads.getReadCount() + " found."); 233 | return unmappedReads; 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/common/GenomicsApiDataSourceFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.google.cloud.genomics.gatk.common; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | /** 22 | * Creates GenomicsApiDataSource objects, one per each root url 23 | * (e.g. https://www.googleapis.com/genomics/v1beta2). 24 | * Allows configuring settings such as client secrets file on a per 25 | * root url basis. 26 | */ 27 | public class GenomicsApiDataSourceFactory { 28 | /** 29 | * Settings required for initializing GenomicsApiDataSource 30 | */ 31 | public static class Settings { 32 | public Settings() { 33 | clientSecretsFile = ""; 34 | noLocalServer = false; 35 | } 36 | public Settings(String clientSecretsFile, boolean noLocalServer) { 37 | this.clientSecretsFile = clientSecretsFile; 38 | this.noLocalServer = noLocalServer; 39 | } 40 | public String clientSecretsFile; 41 | public boolean noLocalServer; 42 | } 43 | 44 | /** 45 | * A pair of settings and the corresponding initialized data source. 46 | */ 47 | private static class Data { 48 | public Data(Settings settings, GenomicsApiDataSource dataSource) { 49 | this.settings = settings; 50 | this.dataSource = dataSource; 51 | } 52 | public Settings settings; 53 | public GenomicsApiDataSource dataSource; 54 | } 55 | 56 | private Map dataSources = new HashMap(); 57 | 58 | /** 59 | * Sets the settings for a given root url, that will be used for creating 60 | * the data source. Has no effect if the data source has already been created. 61 | */ 62 | public void configure(String rootUrl, Settings settings) { 63 | Data data = dataSources.get(rootUrl); 64 | if (data == null) { 65 | data = new Data(settings, null); 66 | dataSources.put(rootUrl, data); 67 | } else { 68 | data.settings = settings; 69 | } 70 | } 71 | 72 | /** 73 | * Lazily creates and returns the data source for a given root url. 74 | */ 75 | public GenomicsApiDataSource get(String rootUrl) { 76 | Data data = dataSources.get(rootUrl); 77 | if (data == null) { 78 | data = new Data(new Settings(), null); 79 | dataSources.put(rootUrl, data); 80 | } 81 | if (data.dataSource == null) { 82 | data.dataSource = new GenomicsApiDataSource(rootUrl, 83 | data.settings.clientSecretsFile, data.settings.noLocalServer); 84 | } 85 | return data.dataSource; 86 | } 87 | 88 | public GenomicsApiDataSourceFactory() { 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/common/ReadIteratorResource.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.google.cloud.genomics.gatk.common; 17 | 18 | import com.google.api.services.genomics.model.Read; 19 | import com.google.api.services.genomics.model.ReadGroupSet; 20 | import com.google.api.services.genomics.model.Reference; 21 | 22 | import htsjdk.samtools.SAMRecordCoordinateComparator; 23 | import htsjdk.samtools.SAMFileHeader; 24 | import htsjdk.samtools.SAMRecord; 25 | 26 | import java.util.ArrayList; 27 | import java.util.Collections; 28 | import java.util.Comparator; 29 | import java.util.Iterator; 30 | import java.util.List; 31 | import java.util.logging.Logger; 32 | 33 | /** 34 | * Provides reads data in the from of SAMRecords and SAMFileHeader, by wrapping 35 | * an existing source of Read and HeaderSection data and doing the conversion using 36 | * GenomicsConverter. 37 | */ 38 | public class ReadIteratorResource { 39 | private static final Logger LOG = Logger.getLogger(ReadIteratorResource.class.getName()); 40 | 41 | private ReadGroupSet readGroupSet; 42 | private SAMFileHeader cachedSAMFileHeader; 43 | private List references; 44 | private Iterable iterable; 45 | private UnmappedReads unmappedReads; 46 | private Iterator unmappedMatesIterator; 47 | private Iterator samePositionIterator; 48 | private SAMRecord recordAtNextPosition; 49 | private static Comparator samRecordCoordinateComparator = new SAMRecordCoordinateComparator(); 50 | 51 | public ReadIteratorResource(ReadGroupSet readGroupSet, List references, 52 | UnmappedReads unmappedReads, 53 | Iterable iterable) { 54 | super(); 55 | this.readGroupSet = readGroupSet; 56 | this.references = references; 57 | this.unmappedReads = unmappedReads; 58 | this.iterable = iterable; 59 | } 60 | 61 | public ReadGroupSet getReadGroupSet() { 62 | return readGroupSet; 63 | } 64 | 65 | public void setReadGroupSet(ReadGroupSet readGroupSet) { 66 | this.readGroupSet = readGroupSet; 67 | } 68 | 69 | public List getReferences() { 70 | return references; 71 | } 72 | 73 | public void setReferences(List references) { 74 | this.references = references; 75 | } 76 | 77 | public Iterable getIterable() { 78 | return iterable; 79 | } 80 | 81 | public void setIterable(Iterable iterable) { 82 | this.iterable = iterable; 83 | } 84 | 85 | public SAMFileHeader getSAMFileHeader() { 86 | if (cachedSAMFileHeader == null) { 87 | cachedSAMFileHeader = 88 | GenomicsConverter.makeSAMFileHeader(getReadGroupSet(), getReferences()); 89 | } 90 | return cachedSAMFileHeader; 91 | } 92 | 93 | public Iterable getSAMRecordIterable() { 94 | final Iterator readIterator = getIterable().iterator(); 95 | final SAMFileHeader header = getSAMFileHeader(); 96 | return new Iterable() { 97 | @Override 98 | public Iterator iterator() { 99 | return new Iterator() { 100 | private SAMRecord nextRecord = peek(); 101 | private Read mappedRead; 102 | private final boolean injectingUnmappedPairsOfMappedRead = 103 | unmappedReads != null; 104 | 105 | @Override 106 | public boolean hasNext() { 107 | return nextRecord != null; 108 | } 109 | 110 | @Override 111 | public SAMRecord next() { 112 | SAMRecord toReturn = nextRecord; 113 | nextRecord = peek(); 114 | return toReturn; 115 | } 116 | 117 | private SAMRecord peek() { 118 | if (!injectingUnmappedPairsOfMappedRead) { 119 | return getNextSAMRecord(); 120 | } 121 | // If we are traversing the list of reads at same position we 122 | // have collected and sorted beforehand, return elements from the list until 123 | // we exhaust it. 124 | if (samePositionIterator != null && samePositionIterator.hasNext()) { 125 | return samePositionIterator.next(); 126 | } 127 | if (recordAtNextPosition == null) { 128 | recordAtNextPosition = getNextSAMRecord(); 129 | if (recordAtNextPosition == null) { 130 | return null; 131 | } 132 | } 133 | 134 | // Fetch more records and if they are all on the same position, 135 | // collect them and sort them in HTSJDK coordinate order 136 | // to satisfy expectations of Picard tools. 137 | ArrayList readsAtSamePosition = null; 138 | SAMRecord currentRecord; 139 | while (true) { 140 | currentRecord = recordAtNextPosition; 141 | recordAtNextPosition = getNextSAMRecord(); 142 | if (recordAtNextPosition != null && 143 | recordAtNextPosition.getAlignmentStart() == currentRecord.getAlignmentStart() && 144 | recordAtNextPosition.getReferenceName() != null && 145 | recordAtNextPosition.getReferenceName().equals(currentRecord.getReferenceName())) { 146 | if (readsAtSamePosition == null) { 147 | readsAtSamePosition = new ArrayList(2); 148 | readsAtSamePosition.add(currentRecord); 149 | } 150 | readsAtSamePosition.add(recordAtNextPosition); 151 | } else { 152 | break; 153 | } 154 | } 155 | if (readsAtSamePosition == null) { 156 | return currentRecord; 157 | } 158 | if (readsAtSamePosition.size() >= 2) { 159 | Collections.sort(readsAtSamePosition, samRecordCoordinateComparator); 160 | } 161 | samePositionIterator = readsAtSamePosition.iterator(); 162 | return samePositionIterator.next(); 163 | } 164 | 165 | /** 166 | * Fetches the next SAMRecord, dealing with Read->SAMRecord 167 | * conversion and fixup of unmapped pairs of mapped reads. 168 | */ 169 | private SAMRecord getNextSAMRecord() { 170 | Read nextRead = getNextRead(); 171 | 172 | if (nextRead == null) { 173 | return null; 174 | } 175 | 176 | SAMRecord record = GenomicsConverter.makeSAMRecord(nextRead, 177 | header); 178 | 179 | // See https://github.com/ga4gh/schemas/issues/224 180 | // We fix up both the mapped read of unmapped mate pair and the mate 181 | // pair itself according to SAM best practices: 182 | // "For a unmapped paired-end or mate-pair read whose mate is mapped, 183 | // the unmapped read should have RNAME and POS identical to its mate." 184 | if (unmappedMatesIterator != null && mappedRead != null) { 185 | if (mappedRead != nextRead) { 186 | record.setReferenceName(mappedRead.getAlignment().getPosition().getReferenceName()); 187 | record.setAlignmentStart(record.getMateAlignmentStart()); 188 | record.setReadNegativeStrandFlag(record.getMateNegativeStrandFlag()); 189 | } else { 190 | record.setMateReferenceName(record.getReferenceName()); 191 | record.setMateAlignmentStart(record.getAlignmentStart()); 192 | record.setMateNegativeStrandFlag(record.getReadNegativeStrandFlag()); 193 | } 194 | } 195 | 196 | return record; 197 | } 198 | 199 | /** 200 | * Fetches next read, dealing with injection of unmapped mate pairs if needed. 201 | */ 202 | private Read getNextRead() { 203 | // Are we iterating through unmapped mates ? 204 | if (unmappedMatesIterator != null) { 205 | if (unmappedMatesIterator.hasNext()) { 206 | return unmappedMatesIterator.next(); 207 | } else { 208 | unmappedMatesIterator = null; 209 | mappedRead = null; 210 | } 211 | } 212 | 213 | Read nextReadToReturn = getNextReadFromMainIterator(); 214 | if (nextReadToReturn == null) { 215 | return null; 216 | } 217 | 218 | // If we have unmapped mates to inject, see if we need to do it now 219 | if (injectingUnmappedPairsOfMappedRead && 220 | UnmappedReads.isMappedMateOfUnmappedRead(nextReadToReturn)) { 221 | final ArrayList unmappedMates = unmappedReads 222 | .getUnmappedMates(nextReadToReturn); 223 | if (unmappedMates != null) { 224 | unmappedMatesIterator = unmappedMates.iterator(); 225 | mappedRead = nextReadToReturn; 226 | } 227 | } 228 | return nextReadToReturn; 229 | } 230 | 231 | /** 232 | * Fetches next read from the underlying iterator, taking care 233 | * to skipped unmapped mate pairs that we have injected elsewhere. 234 | */ 235 | private Read getNextReadFromMainIterator() { 236 | Read result; 237 | if (readIterator.hasNext()) { 238 | result = readIterator.next(); 239 | 240 | if (injectingUnmappedPairsOfMappedRead) { 241 | // If we are going through unmapped reads, skipped the ones 242 | // that are mates of mapped ones - we would have injected them. 243 | while (UnmappedReads.isUnmappedMateOfMappedRead(result)) { 244 | if (readIterator.hasNext()) { 245 | result = readIterator.next(); 246 | } else { 247 | return null; 248 | } 249 | } 250 | } 251 | } else { 252 | return null; 253 | } 254 | return result; 255 | } 256 | 257 | @Override 258 | public void remove() { 259 | LOG.warning("ReadIteratorResource does not implement remove() method"); 260 | } 261 | }; 262 | } 263 | }; 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/common/UnmappedReads.java: -------------------------------------------------------------------------------- 1 | package com.google.cloud.genomics.gatk.common; 2 | 3 | import com.google.api.services.genomics.model.Position; 4 | import com.google.api.services.genomics.model.Read; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.Comparator; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.logging.Logger; 12 | 13 | /** 14 | * In-memory container for unmapped reads, so we can inject them 15 | * at the right positions to satisfy Picard tools expectations of the order, 16 | * which are violated by the current API implementation. 17 | * See https://github.com/ga4gh/schemas/issues/224 18 | * SAM format *best practice* (not requirement), states: 19 | * "For a unmapped paired-end or mate-pair read whose mate is mapped, the unmapped read should have RNAME and POS identical to its mate." 20 | * But the API returns pairs where an unmapped mate has no alignment 21 | * and references its mapped mate and so fails this condition. 22 | * We fix this by reading all unmapped reads and injecting them right after their mapped mates 23 | * as we iterate. 24 | * This is NOT feasible if the number of unmapped reads is very large. 25 | * We detect this condition and if that happens we will output such unmapped 26 | * reads with flags changed to make it look like they are not paired. 27 | * Since most of the tools do precious little with unmapped reads we hope 28 | * we can get away with this. 29 | */ 30 | public class UnmappedReads { 31 | private static final Logger LOG = Logger.getLogger(UnmappedReads.class.getName()); 32 | 33 | /** 34 | * Maximum number of reads we are prepared to keep in memory. 35 | * If this number is exceeded, we will switch to the mode of ignoring 36 | * unmapped mate pairs. 37 | */ 38 | private static final long MAX_READS = 100000000; 39 | 40 | public static boolean isUnmappedMateOfMappedRead(Read read) { 41 | final boolean paired = (read.getNumberReads() != null && 42 | read.getNumberReads() >= 2); 43 | if (!paired) { 44 | return false; 45 | } 46 | final boolean unmapped = (read.getAlignment() == null || 47 | read.getAlignment().getPosition() == null || 48 | read.getAlignment().getPosition().getPosition() == null); 49 | if (!unmapped) { 50 | return false; 51 | } 52 | final Position matePosition = read.getNextMatePosition(); 53 | if (matePosition == null) { 54 | return false; 55 | } 56 | if (read.getFragmentName() == null) { 57 | return false; 58 | } 59 | if (matePosition.getReferenceName() != null && matePosition.getPosition() != null) { 60 | return true; 61 | } 62 | return false; 63 | } 64 | 65 | public static boolean isMappedMateOfUnmappedRead(Read read) { 66 | return read.getNumberReads() > 0 && 67 | (read.getNextMatePosition() == null || 68 | read.getNextMatePosition().getPosition() == null); 69 | } 70 | 71 | /** 72 | * Checks and adds the read if we need to remember it for injection. 73 | * Returns true if the read was added. 74 | */ 75 | public boolean maybeAddRead(Read read) { 76 | if (!isUnmappedMateOfMappedRead(read)) { 77 | return false; 78 | } 79 | final String reference = read.getNextMatePosition().getReferenceName(); 80 | String key = getReadKey(read); 81 | Map> reads = unmappedReads.get(reference); 82 | if (reads == null) { 83 | reads = new HashMap>(); 84 | unmappedReads.put(reference, reads); 85 | } 86 | ArrayList mates = reads.get(key); 87 | if (mates == null) { 88 | mates = new ArrayList(); 89 | reads.put(key, mates); 90 | } 91 | if (getReadCount() < MAX_READS) { 92 | mates.add(read); 93 | readCount++; 94 | return true; 95 | } else { 96 | LOG.warning("Reached the limit of in-memory unmapped mates for injection."); 97 | } 98 | return false; 99 | } 100 | 101 | /** 102 | * Checks if the passed read has unmapped mates that need to be injected and 103 | * if so - returns them. The returned list is sorted by read number to 104 | * handle the case of multi-read fragments. 105 | */ 106 | public ArrayList getUnmappedMates(Read read) { 107 | if (read.getNumberReads() == null || 108 | read.getNumberReads() < 2 || 109 | (read.getNextMatePosition() != null && 110 | read.getNextMatePosition().getPosition() != null) || 111 | read.getAlignment() == null || 112 | read.getAlignment().getPosition() == null || 113 | read.getAlignment().getPosition().getReferenceName() == null || 114 | read.getFragmentName() == null) { 115 | return null; 116 | } 117 | final String reference = read.getAlignment().getPosition().getReferenceName(); 118 | final String key = getReadKey(read); 119 | 120 | Map> reads = unmappedReads.get(reference); 121 | if (reads != null) { 122 | final ArrayList mates = reads.get(key); 123 | if (mates != null && mates.size() > 1) { 124 | Collections.sort(mates, matesComparator); 125 | } 126 | return mates; 127 | } 128 | return null; 129 | } 130 | 131 | public long getReadCount() { 132 | return readCount; 133 | } 134 | 135 | private static String getReadKey(Read read) { 136 | return read.getFragmentName(); 137 | } 138 | 139 | private static Comparator matesComparator = new Comparator() { 140 | @Override 141 | public int compare(Read r1, Read r2) { 142 | return r1.getReadNumber() - r2.getReadNumber(); 143 | } 144 | }; 145 | 146 | private Map>> unmappedReads = 147 | new HashMap>>(); 148 | 149 | private long readCount = 0; 150 | } 151 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/htsjdk/GA4GHQueryInterval.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.google.cloud.genomics.gatk.htsjdk; 17 | 18 | import htsjdk.samtools.util.CoordMath; 19 | import htsjdk.samtools.SAMRecord; 20 | 21 | /** 22 | * Similar to HTSJDK's QueryInterval but allows specifying sequence name 23 | * (as opposed to index in the header) and adds ability to check if a given read 24 | * matches the interval. 25 | */ 26 | public class GA4GHQueryInterval { 27 | private String sequence; 28 | private int start; 29 | private int end; 30 | 31 | public enum ReadPositionConstraint { 32 | OVERLAPPING, 33 | CONTAINED, 34 | START_AT 35 | } 36 | private ReadPositionConstraint readPositionConstraint; 37 | 38 | public GA4GHQueryInterval(String sequence, int start, int end, 39 | ReadPositionConstraint readPositionConstraint) { 40 | super(); 41 | this.sequence = sequence; 42 | this.start = start; 43 | this.end = end; 44 | this.readPositionConstraint = readPositionConstraint; 45 | } 46 | 47 | public String getSequence() { 48 | return sequence; 49 | } 50 | 51 | public void setSequence(String sequence) { 52 | this.sequence = sequence; 53 | } 54 | 55 | public int getStart() { 56 | return start; 57 | } 58 | 59 | public void setStart(int start) { 60 | this.start = start; 61 | } 62 | 63 | public int getEnd() { 64 | return end; 65 | } 66 | 67 | public void setEnd(int end) { 68 | this.end = end; 69 | } 70 | 71 | public ReadPositionConstraint getReadPositionConstraint() { 72 | return readPositionConstraint; 73 | } 74 | 75 | public void setReadPositionConstraint(ReadPositionConstraint readPositionConstraint) { 76 | this.readPositionConstraint = readPositionConstraint; 77 | } 78 | 79 | /** 80 | * Returns true iff the read specified by the record matches the interval 81 | * given the interval's constraints and the read position. 82 | */ 83 | public boolean matches(SAMRecord record) { 84 | int myEnd = end == 0 ? Integer.MAX_VALUE : end; 85 | switch (readPositionConstraint) { 86 | case OVERLAPPING: 87 | return CoordMath.overlaps(start, myEnd, 88 | record.getAlignmentStart(), record.getAlignmentEnd()); 89 | case CONTAINED: 90 | return CoordMath.encloses(start, myEnd, 91 | record.getAlignmentStart(), record.getAlignmentEnd()); 92 | case START_AT: 93 | return start == record.getAlignmentStart(); 94 | } 95 | return false; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/htsjdk/GA4GHReaderFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.google.cloud.genomics.gatk.htsjdk; 17 | 18 | import htsjdk.samtools.CustomReaderFactory; 19 | import htsjdk.samtools.SamReader; 20 | 21 | import java.net.URL; 22 | import java.util.logging.Logger; 23 | /** 24 | * HTSJDK CustomReaderFactory implementation. 25 | * Returns a SamReader that reads data from GA4GH API. 26 | */ 27 | public class GA4GHReaderFactory implements CustomReaderFactory.ICustomReaderFactory { 28 | private static final Logger LOG = Logger.getLogger(GA4GHReaderFactory.class.getName()); 29 | 30 | @Override 31 | public SamReader open(URL url) { 32 | try { 33 | return new GA4GHSamReader(url); 34 | } catch (RuntimeException rex) { 35 | throw rex; 36 | } catch (Exception ex) { 37 | LOG.warning("Error creating SamReader " + ex.toString()); 38 | return null; 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/htsjdk/GA4GHSamReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.google.cloud.genomics.gatk.htsjdk; 17 | 18 | import com.google.cloud.genomics.gatk.common.GA4GHUrl; 19 | import com.google.cloud.genomics.gatk.common.GenomicsApiDataSource; 20 | import com.google.cloud.genomics.gatk.common.GenomicsApiDataSourceFactory; 21 | import com.google.cloud.genomics.gatk.common.GenomicsApiDataSourceFactory.Settings; 22 | 23 | import htsjdk.samtools.QueryInterval; 24 | import htsjdk.samtools.SAMFileHeader; 25 | import htsjdk.samtools.SAMFormatException; 26 | import htsjdk.samtools.SAMRecord; 27 | import htsjdk.samtools.SAMRecordIterator; 28 | import htsjdk.samtools.SamReader; 29 | import htsjdk.samtools.util.CloseableIterator; 30 | 31 | import java.io.IOException; 32 | import java.net.URISyntaxException; 33 | import java.net.URL; 34 | import java.security.GeneralSecurityException; 35 | 36 | /** 37 | * SamReader implementation that reads data from GA4GH API. 38 | * For client_secrets file, specify the path in the ga4gh.client_secrets system property. 39 | */ 40 | public class GA4GHSamReader implements SamReader { 41 | private GA4GHUrl url; 42 | private GenomicsApiDataSourceFactory factory; 43 | GenomicsApiDataSource dataSource; 44 | GA4GHSamRecordIterator iterator; 45 | 46 | public GA4GHSamReader(URL url) throws URISyntaxException, IOException, GeneralSecurityException { 47 | this.url = new GA4GHUrl(url); 48 | this.factory = new GenomicsApiDataSourceFactory(); 49 | factory.configure(this.url.getRootUrl(), 50 | new Settings( 51 | System.getProperty("ga4gh.client_secrets", "client_secrets.json"), 52 | System.getProperty("ga4gh.no_local_server","") 53 | .toLowerCase().equals("true"))); 54 | dataSource = factory.get(this.url.getRootUrl()); 55 | queryOverlapping(this.url.getSequence(), this.url.getRangeStart(), 56 | this.url.getRangeEnd()); 57 | } 58 | 59 | @Override 60 | public void close() throws IOException { 61 | this.dataSource = null; 62 | this.factory = null; 63 | } 64 | 65 | @Override 66 | public SAMFileHeader getFileHeader() { 67 | return iterator.getFileHeader(); 68 | } 69 | 70 | @Override 71 | public Type type() { 72 | // TODO(iliat): figure out bearing of this on indexing 73 | return Type.SAM_TYPE; 74 | } 75 | 76 | @Override 77 | public boolean hasIndex() { 78 | return true; 79 | } 80 | 81 | @Override 82 | public Indexing indexing() { 83 | return null; 84 | } 85 | 86 | @Override 87 | public SAMRecordIterator queryContained(String sequence, int start, int end) { 88 | return query(sequence, start, end, true); 89 | } 90 | 91 | @Override 92 | public SAMRecordIterator queryOverlapping(String sequence, int start, int end) { 93 | return query(sequence, start, end, false); 94 | } 95 | 96 | @Override 97 | public SAMRecordIterator queryContained(QueryInterval[] intervals) { 98 | return query(intervals, true); 99 | } 100 | 101 | @Override 102 | public SAMRecordIterator queryOverlapping(QueryInterval[] intervals) { 103 | return query(intervals, false); 104 | } 105 | 106 | @Override 107 | public SAMRecordIterator queryUnmapped() { 108 | return queryOverlapping("*", 0, 0); 109 | } 110 | 111 | @Override 112 | public SAMRecordIterator query(QueryInterval[] intervals, boolean contained) { 113 | final GA4GHQueryInterval[] myIntervals = new GA4GHQueryInterval[intervals.length]; 114 | final GA4GHQueryInterval.ReadPositionConstraint constraint = contained ? 115 | GA4GHQueryInterval.ReadPositionConstraint.CONTAINED : 116 | GA4GHQueryInterval.ReadPositionConstraint.OVERLAPPING; 117 | final SAMFileHeader header = getFileHeader(); 118 | for (int i = 0; i < intervals.length; i++) { 119 | final QueryInterval interval = intervals[i]; 120 | final String sequence = header.getSequence( 121 | interval.referenceIndex).getSequenceName(); 122 | myIntervals[i] = new GA4GHQueryInterval(sequence, interval.start, 123 | interval.end, constraint); 124 | } 125 | return query(myIntervals); 126 | } 127 | 128 | @Override 129 | public SAMRecordIterator query(String sequence, int start, int end, boolean contained) { 130 | return query(new GA4GHQueryInterval[] { 131 | new GA4GHQueryInterval(sequence, start, end, contained ? 132 | GA4GHQueryInterval.ReadPositionConstraint.CONTAINED : 133 | GA4GHQueryInterval.ReadPositionConstraint.OVERLAPPING)}); 134 | } 135 | 136 | @Override 137 | public SAMRecordIterator queryAlignmentStart(String sequence, int start) { 138 | return query(new GA4GHQueryInterval[] { 139 | new GA4GHQueryInterval(sequence, start, 0, 140 | GA4GHQueryInterval.ReadPositionConstraint.START_AT)}); 141 | } 142 | 143 | @Override 144 | public SAMRecord queryMate(SAMRecord rec) { 145 | if (!rec.getReadPairedFlag()) { 146 | throw new IllegalArgumentException("queryMate called for unpaired read."); 147 | } 148 | if (rec.getFirstOfPairFlag() == rec.getSecondOfPairFlag()) { 149 | throw new IllegalArgumentException( 150 | "SAMRecord must be either first and second of pair, but not both."); 151 | } 152 | final boolean firstOfPair = rec.getFirstOfPairFlag(); 153 | final CloseableIterator it; 154 | if (rec.getMateReferenceIndex() == SAMRecord.NO_ALIGNMENT_REFERENCE_INDEX) { 155 | it = queryUnmapped(); 156 | } else { 157 | it = queryAlignmentStart(rec.getMateReferenceName(), rec.getMateAlignmentStart()); 158 | } 159 | try { 160 | SAMRecord mateRec = null; 161 | while (it.hasNext()) { 162 | final SAMRecord next = it.next(); 163 | if (!next.getReadPairedFlag()) { 164 | if (rec.getReadName().equals(next.getReadName())) { 165 | throw new SAMFormatException( 166 | "Paired and unpaired reads with same name: " + rec.getReadName()); 167 | } 168 | continue; 169 | } 170 | if (firstOfPair) { 171 | if (next.getFirstOfPairFlag()) { 172 | continue; 173 | } 174 | } else { 175 | if (next.getSecondOfPairFlag()) { 176 | continue; 177 | } 178 | } 179 | if (rec.getReadName().equals(next.getReadName())) { 180 | if (mateRec != null) { 181 | throw new SAMFormatException( 182 | "Multiple SAMRecord with read name " + rec.getReadName() + 183 | " for " + (firstOfPair ? "second" : "first") + " end."); 184 | } 185 | mateRec = next; 186 | } 187 | } 188 | return mateRec; 189 | } finally { 190 | it.close(); 191 | } 192 | } 193 | 194 | public SAMRecordIterator query(GA4GHQueryInterval[] intervals) { 195 | iterator = new GA4GHSamRecordIterator(dataSource, url.getReadset(), 196 | intervals); 197 | return iterator(); 198 | } 199 | 200 | @Override 201 | public SAMRecordIterator iterator() { 202 | return iterator; 203 | } 204 | 205 | @Override 206 | public String getResourceDescription() { 207 | return "GA4GH API"; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/htsjdk/GA4GHSamRecordIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.google.cloud.genomics.gatk.htsjdk; 17 | 18 | import com.google.cloud.genomics.gatk.common.GenomicsApiDataSource; 19 | import com.google.cloud.genomics.gatk.common.ReadIteratorResource; 20 | import com.google.common.base.Stopwatch; 21 | 22 | import htsjdk.samtools.SAMFileHeader; 23 | import htsjdk.samtools.SAMFileHeader.SortOrder; 24 | import htsjdk.samtools.SAMRecord; 25 | import htsjdk.samtools.SAMRecordIterator; 26 | 27 | import java.util.concurrent.TimeUnit; 28 | import java.util.Iterator; 29 | import java.util.logging.Logger; 30 | 31 | /** 32 | * Wraps iterators provided from Genomics API and implements 33 | * HTSJDK's SAMRecordIterator. 34 | * Iterates over data returned from the API and when needed 35 | * re-queries the API for more data. 36 | * Since the API always return *overlapping* reads and SAMRecordIterator 37 | * supports contained and start-at queries, this class filters reads 38 | * returned from the API to make sure they conform to the requested intervals. 39 | */ 40 | public class GA4GHSamRecordIterator implements SAMRecordIterator{ 41 | private static final Logger LOG = Logger.getLogger(GA4GHSamRecordIterator.class.getName()); 42 | 43 | private static final long STATS_DUMP_INTERVAL_READS = 100000; 44 | 45 | Iterator iterator; 46 | GenomicsApiDataSource dataSource; 47 | GA4GHQueryInterval[] intervals; 48 | String readSetId; 49 | int intervalIndex = -1; 50 | boolean hasNext; 51 | SAMRecord nextRead; 52 | SAMFileHeader header; 53 | long processedReads; 54 | Stopwatch timer; 55 | 56 | public GA4GHSamRecordIterator(GenomicsApiDataSource dataSource, 57 | String readSetId, 58 | GA4GHQueryInterval[] intervals) { 59 | this.dataSource = dataSource; 60 | this.readSetId = readSetId; 61 | this.intervals = intervals; 62 | this.timer = Stopwatch.createUnstarted(); 63 | seekMatchingRead(); 64 | } 65 | 66 | /** Returns true when we truly reached the end of all requested data */ 67 | boolean isAtEnd() { 68 | return intervals == null || intervals.length == 0 || 69 | intervalIndex >= intervals.length; 70 | } 71 | 72 | /** Returns the current interval being processed or null if we have reached the end */ 73 | GA4GHQueryInterval currentInterval() { 74 | if (isAtEnd()) { 75 | return null; 76 | } 77 | return intervals[intervalIndex]; 78 | } 79 | 80 | /** Re-queries the API for the next interval */ 81 | ReadIteratorResource queryNextInterval() { 82 | Stopwatch w = Stopwatch.createStarted(); 83 | if (!isAtEnd()) { 84 | intervalIndex++; 85 | } 86 | if (isAtEnd()) { 87 | return null; 88 | } 89 | ReadIteratorResource result = queryForInterval(currentInterval()); 90 | LOG.info("Interval query took: " + w); 91 | startTiming(); 92 | return result; 93 | } 94 | 95 | /** Queries the API for an interval and returns the iterator resource, or null if failed */ 96 | ReadIteratorResource queryForInterval(GA4GHQueryInterval interval) { 97 | try { 98 | return dataSource.getReadsFromGenomicsApi(readSetId, interval.getSequence(), 99 | interval.getStart(), interval.getEnd()); 100 | } catch (Exception ex) { 101 | LOG.warning("Error getting data for interval " + ex.toString()); 102 | } 103 | return null; 104 | } 105 | 106 | /** 107 | * Ensures next returned read will match the currently requested interval. 108 | * Since the API always returns overlapping reads we might need to skip some 109 | * reads if the interval asks for "included" or "starts at" types. 110 | * Also deals with the case of iterator being at an end and needing to query 111 | * for the next interval. 112 | */ 113 | void seekMatchingRead() { 114 | while (!isAtEnd()) { 115 | if (iterator == null || !iterator.hasNext()) { 116 | LOG.info("Getting " + 117 | (iterator == null ? "first" : "next") + 118 | "interval from the API"); 119 | // We have hit an end (or this is first time) so we need to go fish 120 | // to the API. 121 | ReadIteratorResource resource = queryNextInterval(); 122 | if (resource != null) { 123 | LOG.info("Got next interval from the API"); 124 | header = resource.getSAMFileHeader(); 125 | iterator = resource.getSAMRecordIterable().iterator(); 126 | } else { 127 | LOG.info("Failed to get next interval from the API"); 128 | header = null; 129 | iterator = null; 130 | } 131 | } else { 132 | nextRead = iterator.next(); 133 | if (currentInterval().matches(nextRead)) { 134 | return; // Happy case, otherwise we keep spinning in the loop. 135 | } else { 136 | LOG.info("Skipping non matching read"); 137 | } 138 | } 139 | } 140 | } 141 | 142 | 143 | @Override 144 | public void close() { 145 | this.iterator = null; 146 | this.dataSource = null; 147 | this.intervalIndex = intervals.length; 148 | } 149 | 150 | @Override 151 | public boolean hasNext() { 152 | return !isAtEnd(); 153 | } 154 | 155 | @Override 156 | public SAMRecord next() { 157 | SAMRecord retVal = nextRead; 158 | seekMatchingRead(); 159 | updateTiming(); 160 | return retVal; 161 | } 162 | 163 | @Override 164 | public void remove() { 165 | // Not implemented 166 | } 167 | 168 | @Override 169 | public SAMRecordIterator assertSorted(SortOrder sortOrder) { 170 | // TODO(iliat): implement this properly. This code never checks anything. 171 | return this; 172 | } 173 | 174 | public SAMFileHeader getFileHeader() { 175 | return header; 176 | } 177 | 178 | void startTiming() { 179 | processedReads = 0; 180 | timer.start(); 181 | } 182 | 183 | void updateTiming() { 184 | processedReads++; 185 | if ((processedReads % STATS_DUMP_INTERVAL_READS) == 0) { 186 | dumpTiming(); 187 | } 188 | } 189 | 190 | void stopTiming() { 191 | timer.stop(); 192 | } 193 | 194 | void dumpTiming() { 195 | if (processedReads == 0) { 196 | return; 197 | } 198 | LOG.info("Processed " + processedReads + " reads in " + timer + 199 | ". Speed: " + (processedReads*1000)/timer.elapsed(TimeUnit.MILLISECONDS) + " reads/sec"); 200 | 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/htsjdk/SamReaderExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.google.cloud.genomics.gatk.htsjdk; 17 | 18 | import htsjdk.samtools.SAMRecord; 19 | import htsjdk.samtools.SamInputResource; 20 | import htsjdk.samtools.SamReader; 21 | import htsjdk.samtools.SamReaderFactory; 22 | 23 | import java.net.MalformedURLException; 24 | import java.net.URL; 25 | 26 | /** 27 | * Example of HTSJDK SamReader and SamReaderFactory class usage. 28 | * Illustrates how to plug in a custom SamReaderFactory in order to consume 29 | * data from ga4gh URLs. 30 | * 31 | * To run this we need to specify the custom reader factory for HTSJDK and set 32 | * client_secrets file path for Genomics API: 33 | * -Dsamjdk.custom_reader=https://www.googleapis.com/genomics,com.google.cloud.genomics.gatk.htsjdk.GA4GHReaderFactory 34 | * -Dga4gh.client_secrets= 35 | */ 36 | public class SamReaderExample { 37 | static String GA4GH_URL = 38 | "https://www.googleapis.com/genomics/v1beta2/readgroupsets/CLqN8Z3sDRCwgrmdkOXjn_sB/*/"; 39 | 40 | public static void main(String[] args) { 41 | try { 42 | SamReaderFactory factory = SamReaderFactory.makeDefault(); 43 | 44 | // If it was a file, we would open like so: 45 | // factory.open(new File("~/testdata/htsjdk/samtools/uncompressed.sam")); 46 | // For API access we use SamInputResource constructed from a URL: 47 | SamReader reader = factory.open(SamInputResource.of(new URL(GA4GH_URL))); 48 | 49 | for (final SAMRecord samRecord : reader) { 50 | System.err.println(samRecord); 51 | } 52 | 53 | } catch (MalformedURLException e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/picard/runner/GA4GHPicardRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.google.cloud.genomics.gatk.picard.runner; 17 | 18 | import com.google.cloud.genomics.gatk.common.GA4GHUrl; 19 | import com.google.cloud.genomics.gatk.common.GenomicsApiDataSourceFactory; 20 | import com.google.cloud.genomics.gatk.common.GenomicsApiDataSourceFactory.Settings; 21 | import com.google.cloud.genomics.gatk.common.ReadIteratorResource; 22 | 23 | import com.beust.jcommander.JCommander; 24 | import com.beust.jcommander.Parameter; 25 | import com.beust.jcommander.Parameters; 26 | 27 | import htsjdk.samtools.SamReader; 28 | import htsjdk.samtools.SamReaderFactory; 29 | 30 | import java.io.File; 31 | import java.io.IOException; 32 | import java.io.OutputStream; 33 | import java.net.URISyntaxException; 34 | import java.security.GeneralSecurityException; 35 | import java.util.ArrayList; 36 | import java.util.List; 37 | import java.util.logging.Logger; 38 | 39 | /** 40 | * Main class for running Picard tools with INPUTS using ga4gh:// urls. 41 | */ 42 | @Parameters(separators = "=") 43 | public class GA4GHPicardRunner { 44 | private static final Logger LOG = Logger.getLogger(GA4GHPicardRunner.class.getName()); 45 | @Parameter(names = "--root_url", 46 | description = "set the Genomics API root URL", 47 | hidden = true) 48 | public String rootUrl = "https://www.googleapis.com/genomics/v1beta2"; 49 | 50 | @Parameter(names = "--nolocalserver", 51 | description = "Disable the starting up of a local server for the auth flows", 52 | hidden = true) 53 | public boolean noLocalServer = false; 54 | 55 | @Parameter(names = "--client_secrets_filename", 56 | description = "Path to client_secrets.json") 57 | public String clientSecretsFilename = "client_secrets.json"; 58 | 59 | @Parameter(names = "-path", 60 | description = "Path to picard tools binaries") 61 | public String picardPath = "picard/dist"; 62 | 63 | @Parameter(names = "-tool", 64 | required= true, 65 | description = "Name of the Picard tool to run") 66 | public String picardTool = ""; 67 | 68 | @Parameter(names = "-jvm_args", 69 | description = "JVM args for Picard tool run") 70 | public String picardJVMArgs = "-Xmx4g"; 71 | 72 | // TODO(iliat): support multiple inputs 73 | @Parameter(description = 74 | "Picard tool parameters, INPUT(s) can be files or GA4GH urls.") 75 | public List picardArgs = new ArrayList(); 76 | 77 | @Parameter(names = "-pipeFiles", 78 | description = "Pipe local files too") 79 | public Boolean pipeFiles = true; 80 | 81 | static String INPUT_PREFIX = "INPUT="; 82 | 83 | static String STDIN_FILE_NAME = "/dev/stdin"; 84 | 85 | /** Cmd line arguments array for Picard tool invocation */ 86 | private ArrayList command = new ArrayList(); 87 | 88 | /** List of INPUT=... parameters to process and potentially pipe through */ 89 | private ArrayList inputs = new ArrayList(); 90 | 91 | /** Picard process */ 92 | private Process process; 93 | 94 | /** Factory for creating Genomics Api based data sources */ 95 | private GenomicsApiDataSourceFactory factory = new GenomicsApiDataSourceFactory(); 96 | 97 | /** 98 | * Holds all relevant data for one input resource. 99 | */ 100 | @SuppressWarnings("unused") 101 | private static class Input { 102 | public Input(String resource, String pipeName, SAMFilePump pump) { 103 | super(); 104 | this.resource = resource; 105 | this.pipeName = pipeName; 106 | this.pump = pump; 107 | } 108 | 109 | public String getResource() { 110 | return resource; 111 | } 112 | public void setResource(String resource) { 113 | this.resource = resource; 114 | } 115 | public String getPipeName() { 116 | return pipeName; 117 | } 118 | public void setPipeName(String pipeName) { 119 | this.pipeName = pipeName; 120 | } 121 | public SAMFilePump getPump() { 122 | return pump; 123 | } 124 | public void setPump(SAMFilePump pump) { 125 | this.pump = pump; 126 | } 127 | private String resource; 128 | private String pipeName; 129 | private SAMFilePump pump; 130 | } 131 | 132 | /** Runs the program */ 133 | public static void main(String[] args) { 134 | (new GA4GHPicardRunner()).run(args); 135 | } 136 | 137 | /** Trivial constructor */ 138 | public GA4GHPicardRunner() { 139 | } 140 | 141 | /** Sets up required streams and pipes and then spawns the Picard tool */ 142 | public void run(String[] args) { 143 | LOG.info("Starting GA4GHPicardRunner"); 144 | try { 145 | parseCmdLine(args); 146 | buildPicardCommand(); 147 | startProcess(); 148 | pumpInputData(); 149 | waitForProcessEnd(); 150 | } catch (Exception e) { 151 | System.out.println(e.getMessage()); 152 | e.printStackTrace(); 153 | } 154 | } 155 | 156 | /** Parses cmd line with JCommander */ 157 | void parseCmdLine(String[] args) { 158 | JCommander parser = new JCommander(this, args); 159 | parser.setProgramName("GA4GHPicardRunner"); 160 | LOG.info("Cmd line parsed"); 161 | } 162 | 163 | /** 164 | * Adds relevant parts to the cmd line for Picard tool, finds and extracts 165 | * "INPUT=" arguments and processes them by creating appropriate data pumps. 166 | */ 167 | private void buildPicardCommand() 168 | throws IOException, GeneralSecurityException, URISyntaxException { 169 | File picardJarPath = new File(picardPath, "picard.jar"); 170 | if (!picardJarPath.exists()) { 171 | throw new IOException("Picard tool not found at " + 172 | picardJarPath.getAbsolutePath()); 173 | } 174 | 175 | command.add("java"); 176 | command.add(picardJVMArgs); 177 | command.add("-jar"); 178 | command.add(picardJarPath.getAbsolutePath()); 179 | command.add(picardTool); 180 | 181 | for (String picardArg : picardArgs) { 182 | if (picardArg.startsWith(INPUT_PREFIX)) { 183 | String inputPath = picardArg.substring(INPUT_PREFIX.length()); 184 | inputs.add(processInput(inputPath)); 185 | } else { 186 | command.add(picardArg); 187 | } 188 | } 189 | for (Input input : inputs) { 190 | command.add("INPUT=" + input.pipeName); 191 | } 192 | } 193 | 194 | private Input processInput(String input) throws IOException, GeneralSecurityException, URISyntaxException { 195 | if (GA4GHUrl.isGA4GHUrl(input)) { 196 | return processGA4GHInput(input); 197 | } else { 198 | return processRegularFileInput(input); 199 | } 200 | } 201 | 202 | /** Processes GA4GH based input, creates required API connections and data pump */ 203 | private Input processGA4GHInput(String input) throws IOException, GeneralSecurityException, URISyntaxException { 204 | GA4GHUrl url = new GA4GHUrl(input); 205 | factory.configure(url.getRootUrl(), 206 | new Settings(clientSecretsFilename, noLocalServer)); 207 | ReadIteratorResource reads = factory 208 | .get(url.getRootUrl()) 209 | .getReadsFromGenomicsApi(url); 210 | return new Input(input, STDIN_FILE_NAME, 211 | new ReadIteratorToSAMFilePump(reads)); 212 | } 213 | 214 | /** Processes regular, non GA4GH based file input */ 215 | private Input processRegularFileInput(String input) throws IOException { 216 | File inputFile = new File(input); 217 | if (!inputFile.exists()) { 218 | throw new IOException("Input does not exist: " + input); 219 | } 220 | if (pipeFiles) { 221 | SamReader samReader = SamReaderFactory.makeDefault().open(inputFile); 222 | return new Input(input, STDIN_FILE_NAME, 223 | new SamReaderToSAMFilePump(samReader)); 224 | } else { 225 | return new Input(input, input, null); 226 | } 227 | } 228 | 229 | /** 230 | * Starts the Picard tool process based on constructed command. 231 | * @throws IOException 232 | */ 233 | private void startProcess() throws IOException { 234 | LOG.info("Building process"); 235 | ProcessBuilder processBuilder = new ProcessBuilder(command); 236 | processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT); 237 | processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT); 238 | 239 | LOG.info("Starting process"); 240 | process = processBuilder.start(); 241 | LOG.info("Process started"); 242 | } 243 | 244 | /** 245 | * Loops through inputs and for each, pumps the data into the proper pipe 246 | * stream connected to the executing process. 247 | * @throws IOException 248 | */ 249 | private void pumpInputData() throws IOException { 250 | for (Input input : inputs) { 251 | if (input.pump == null) { 252 | continue; 253 | } 254 | OutputStream os; 255 | if (input.pipeName.equals(STDIN_FILE_NAME)) { 256 | os = process.getOutputStream(); 257 | } else { 258 | throw new IOException("Only stdin piping is supported so far."); 259 | } 260 | input.pump.pump(os); 261 | } 262 | } 263 | 264 | private void waitForProcessEnd() throws InterruptedException, Exception { 265 | if (process.waitFor() != 0 || process.exitValue() != 0) { 266 | throw new Exception("Picard tool run failed, exit value=" + 267 | process.exitValue()); 268 | } 269 | 270 | LOG.info("Process finished"); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/picard/runner/ReadIteratorToSAMFilePump.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.google.cloud.genomics.gatk.picard.runner; 17 | 18 | import com.google.cloud.genomics.gatk.common.ReadIteratorResource; 19 | 20 | import htsjdk.samtools.SAMFileWriter; 21 | import htsjdk.samtools.SAMFileWriterFactory; 22 | import htsjdk.samtools.SAMRecord; 23 | 24 | import java.io.OutputStream; 25 | 26 | /** 27 | * Writes contents of the ReadIteratorResource into the stream as a SAM file. 28 | */ 29 | public class ReadIteratorToSAMFilePump implements SAMFilePump { 30 | private ReadIteratorResource readIterator; 31 | 32 | public ReadIteratorToSAMFilePump(ReadIteratorResource readIterator) { 33 | this.readIterator = readIterator; 34 | } 35 | 36 | @Override 37 | public void pump(OutputStream out) { 38 | final SAMFileWriter outputSam = new SAMFileWriterFactory().makeSAMWriter( 39 | readIterator.getSAMFileHeader(), true, out); 40 | 41 | for (final SAMRecord samRecord : readIterator.getSAMRecordIterable()) { 42 | outputSam.addAlignment(samRecord); 43 | } 44 | 45 | outputSam.close(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/picard/runner/SAMFilePump.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.google.cloud.genomics.gatk.picard.runner; 17 | 18 | import java.io.IOException; 19 | import java.io.OutputStream; 20 | 21 | /** 22 | * Pumps data from some source into an output stream as a SAM File. 23 | */ 24 | public interface SAMFilePump { 25 | public void pump(OutputStream out) throws IOException; 26 | } 27 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/java/com/google/cloud/genomics/gatk/picard/runner/SamReaderToSAMFilePump.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.google.cloud.genomics.gatk.picard.runner; 17 | 18 | import htsjdk.samtools.SamReader; 19 | import htsjdk.samtools.SAMFileWriter; 20 | import htsjdk.samtools.SAMFileWriterFactory; 21 | import htsjdk.samtools.SAMRecord; 22 | 23 | import java.io.IOException; 24 | import java.io.OutputStream; 25 | 26 | /** 27 | * Reads SAM data using SamReader and pumps it into and OutputStream as a 28 | * SAM File. 29 | * This class is useful to test the piping of SAM data to Picard tools 30 | * without involving actual reading of data through Genomics APIs. 31 | */ 32 | public class SamReaderToSAMFilePump implements SAMFilePump { 33 | private SamReader reader; 34 | 35 | public SamReaderToSAMFilePump(SamReader reader) { 36 | this.reader = reader; 37 | } 38 | 39 | @Override 40 | public void pump(OutputStream out) throws IOException { 41 | final SAMFileWriter outputSam = new SAMFileWriterFactory().makeSAMWriter( 42 | reader.getFileHeader(), true, out); 43 | 44 | for (final SAMRecord samRecord : reader) { 45 | outputSam.addAlignment(samRecord); 46 | } 47 | 48 | outputSam.close(); 49 | reader.close(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/scripts/mark_duplicates.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | OUTPUT_FILE=$(readlink -f ../../../../ex1_deduped_picard_api.bam) 3 | METRICS_FILE=$(readlink -f ../../../../ex1_deduped_picard_api.metrics) 4 | 5 | ./run_picard.sh \ 6 | MarkDuplicates \ 7 | INPUT=https://www.googleapis.com/genomics/v1beta2/readgroupsets/CK256frpGBD44IWHwLP22R4/ \ 8 | OUTPUT=$OUTPUT_FILE \ 9 | METRICS_FILE=$METRICS_FILE 10 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/scripts/mark_duplicates_cigar.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | OUTPUT_FILE=$(readlink -f ../../../../ex1_deduped_picard_cigar_api.bam) 3 | METRICS_FILE=$(readlink -f ../../../../ex1_deduped_picard_cigar_api.metrics) 4 | 5 | ./run_picard.sh \ 6 | MarkDuplicatesWithMateCigar \ 7 | INPUT=https://www.googleapis.com/genomics/v1beta2/readgroupsets/CK256frpGBD44IWHwLP22R4/ \ 8 | OUTPUT=$OUTPUT_FILE \ 9 | METRICS_FILE=$METRICS_FILE 10 | -------------------------------------------------------------------------------- /gatk-tools-java/src/main/scripts/run_picard.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Runs Picard tool specified on the command line, using GA4GH custom reader 3 | # for getting the data from url based INPUTs. 4 | # E.g. run_picard.sh ViewSam INPUT=. 5 | # Assumes directory structure where gatk-tools-java and picard repos reside 6 | # in the same folder and client_secrets is in the same folder: 7 | # .../... 8 | # /gatk-tools-java 9 | # /picard 10 | # /client_secrets.json 11 | # If your setup is different, please modify paths below. 12 | GATK_TOOLS_JAVA_JAR=$(readlink -f ../../../dist/gatk-tools-java-1.0.jar) 13 | CLIENT_SECRETS=$(readlink -f ../../../../client_secrets.json) 14 | PICARD_JAR=$(readlink -f ../../../../picard/dist/picard.jar) 15 | 16 | echo Running Picard form $PICARD_JAR 17 | echo Using gatk-tools-java from $GATK_TOOLS_JAVA_JAR 18 | echo Using client_secrets form $CLIENT_SECRETS 19 | 20 | java -jar \ 21 | -Dsamjdk.custom_reader=https://www.googleapis.com/genomics,\ 22 | com.google.cloud.genomics.gatk.htsjdk.GA4GHReaderFactory,\ 23 | $GATK_TOOLS_JAVA_JAR \ 24 | -Dga4gh.client_secrets=$CLIENT_SECRETS \ 25 | $PICARD_JAR \ 26 | "$@" \ 27 | VERBOSITY=DEBUG QUIET=false -------------------------------------------------------------------------------- /gatk-tools-java/src/main/scripts/view_sam_file.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ./run_picard.sh \ 3 | ViewSam \ 4 | INPUT=https://www.googleapis.com/genomics/v1beta2/readgroupsets/CK256frpGBD44IWHwLP22R4/ 5 | -------------------------------------------------------------------------------- /gatk-tools-java/testdata/ex1_sorted.bam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/gatk-tools-java/testdata/ex1_sorted.bam -------------------------------------------------------------------------------- /gatk-tools-java/testdata/unmapped_mate.bam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/gatk-tools-java/testdata/unmapped_mate.bam -------------------------------------------------------------------------------- /gatk-tools-java/testdata/unmapped_mate.sam: -------------------------------------------------------------------------------- 1 | @HD VN:1.4 SO:coordinate 2 | @SQ SN:seq1 LN:5000 3 | EAS188_7:2:218:877:489 163 seq1 80 10 35M = 250 205 TGTGGGGGCCGCAGTGGCTGGGGGGGGGCGGGCGG <<<<<<<;<<<07640<2<9(<9<<&9%(<(6%%3 MF:i:18 Aq:i:10 NM:i:6 UQ:i:34 H0:i:0 H1:i:0 4 | EAS51_64:7:242:862:732 73 seq1 95 66 35M = 95 0 GGCTGAGGGGTGCAGAGCCGAGTCACGGGGTTGCC <<<<<<<<<<<<<<<;<<<:<;+<3<::3<';:'; MF:i:64 Aq:i:0 NM:i:0 UQ:i:0 H0:i:1 H1:i:0 5 | EAS51_64:7:242:862:732 133 seq1 95 0 * = 95 0 GGGTCTATGTGAACAAAGGCACTAAACACAGCTGT <<<<<<<<<<8<<<<<78<<<378<<<77755++2 MF:i:192 6 | -------------------------------------------------------------------------------- /transcoders/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/transcoders/db.sqlite3 -------------------------------------------------------------------------------- /transcoders/hmdna/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/transcoders/hmdna/__init__.py -------------------------------------------------------------------------------- /transcoders/hmdna/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/transcoders/hmdna/__init__.pyc -------------------------------------------------------------------------------- /transcoders/hmdna/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for hmdna project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.8.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.8/ref/settings/ 11 | """ 12 | 13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 14 | import os 15 | 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'u^*oig+!-61*tzt#fecxvr#8q8ts+nvmghjn_c5j-_ec(**01b' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = ( 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | ) 41 | 42 | MIDDLEWARE_CLASSES = ( 43 | 'django.contrib.sessions.middleware.SessionMiddleware', 44 | 'django.middleware.common.CommonMiddleware', 45 | 'django.middleware.csrf.CsrfViewMiddleware', 46 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 47 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 48 | 'django.contrib.messages.middleware.MessageMiddleware', 49 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 50 | 'django.middleware.security.SecurityMiddleware', 51 | ) 52 | 53 | ROOT_URLCONF = 'hmdna.urls' 54 | 55 | TEMPLATES = [ 56 | { 57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 58 | 'DIRS': [], 59 | 'APP_DIRS': True, 60 | 'OPTIONS': { 61 | 'context_processors': [ 62 | 'django.template.context_processors.debug', 63 | 'django.template.context_processors.request', 64 | 'django.contrib.auth.context_processors.auth', 65 | 'django.contrib.messages.context_processors.messages', 66 | ], 67 | }, 68 | }, 69 | ] 70 | 71 | WSGI_APPLICATION = 'hmdna.wsgi.application' 72 | 73 | 74 | # Database 75 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 76 | 77 | DATABASES = { 78 | 'default': { 79 | 'ENGINE': 'django.db.backends.sqlite3', 80 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 81 | } 82 | } 83 | 84 | 85 | # Internationalization 86 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 87 | 88 | LANGUAGE_CODE = 'en-us' 89 | 90 | TIME_ZONE = 'UTC' 91 | 92 | USE_I18N = True 93 | 94 | USE_L10N = True 95 | 96 | USE_TZ = True 97 | 98 | 99 | # Static files (CSS, JavaScript, Images) 100 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 101 | 102 | STATIC_URL = '/static/' 103 | -------------------------------------------------------------------------------- /transcoders/hmdna/settings.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/transcoders/hmdna/settings.pyc -------------------------------------------------------------------------------- /transcoders/hmdna/urls.py: -------------------------------------------------------------------------------- 1 | """hmdna URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.8/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Add an import: from blog import urls as blog_urls 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) 15 | """ 16 | from django.conf.urls import include, url 17 | from django.contrib import admin 18 | 19 | urlpatterns = [ 20 | url(r'^admin/', include(admin.site.urls)), 21 | ] 22 | -------------------------------------------------------------------------------- /transcoders/hmdna/urls.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/transcoders/hmdna/urls.pyc -------------------------------------------------------------------------------- /transcoders/hmdna/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for hmdna project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "hmdna.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /transcoders/hmdna/wsgi.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcl-io/hmDNA/4f4b14d45e5d4a70f22012984535f2ffeaae4fa9/transcoders/hmdna/wsgi.pyc -------------------------------------------------------------------------------- /transcoders/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "hmdna.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | --------------------------------------------------------------------------------