├── vcffile └── testvcf2.vcf.gz ├── README.md └── genome-server.py /vcffile/testvcf2.vcf.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joepickrell/genome-server-21/HEAD/vcffile/testvcf2.vcf.gz -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # genome-server-21 2 | 3 | Testing how to use the 21 bitcoin computer to sell API calls to your genotypes and phenotypes. Genotypes/phenotypes stored in sqlite database, also can sell bulk access to a VCF file. 4 | 5 | Setup 6 | ----- 7 | 8 | 1. configure 9 | 10 | ``` 11 | $ git clone https://github.com/joepickrell/genome-server-21.git 12 | 13 | $ cd genome-server-21 14 | 15 | $ sudo pip3 install Flask-SQLAlchemy 16 | ``` 17 | 2. make the database of genotypes/phenotypes, serve 18 | ``` 19 | $ python3 genome-server.py vcffile/testvcf2.vcf.gz 20 | ``` 21 | 3. Is it working? 22 | ``` 23 | $ curl -i http://localhost:5000/phenotypes 24 | ``` 25 | You should get a JSON list of phenotypes, like 26 | 27 | ``` 28 | HTTP/1.0 200 OK 29 | Content-Type: application/json 30 | Content-Length: 224 31 | Server: Werkzeug/0.11.2 Python/3.4.2 32 | Date: Fri, 04 Dec 2015 03:30:13 GMT 33 | 34 | { 35 | "pheno_list": [ 36 | { 37 | "pheno_name": "height (cm)", 38 | "uri": "http://localhost:5000/buyphenotype/1" 39 | }, 40 | { 41 | "pheno_name": "weight (lb)", 42 | "uri": "http://localhost:5000/buyphenotype/2" 43 | } 44 | ] 45 | } 46 | ``` 47 | If you try to see the actual value of the phenotype, however: 48 | 49 | ``` 50 | $ curl -i http://localhost:5000/buyphenotype/1 51 | ``` 52 | 53 | this will fail with error 402: payment required (1 satoshi). Using the 21 libraries, this can be successfully requested with the following python code: 54 | 55 | ``` 56 | # Import methods from the 21 Bitcoin Library 57 | from two1.commands.config import Config 58 | from two1.lib.wallet import Wallet 59 | from two1.lib.bitrequests import BitTransferRequests 60 | 61 | # Configure your Bitcoin wallet. 62 | username = Config().username 63 | wallet = Wallet() 64 | requests = BitTransferRequests(wallet, username) 65 | 66 | test = requests.get(url="http://localhost:5000/buyphenotype/1") 67 | print(test.content) 68 | ``` 69 | 70 | Anyone can thus give me 1 satoshi to know my height (or weight, or any genotype from the database). 71 | 72 | # Endpoints 73 | 74 | GET /phenotypes : list of phenotypes, uri for each 75 | 76 | GET /variants : list of genotypes, uri for each 77 | 78 | GET /vcf : list of VCF files 79 | 80 | GET [cost: 1 satoshi] /buyvariant/[chr]/[pos] : returns the VCF genotype and genotype likelihoods at chromosome chr and position pos 81 | 82 | GET [cost: 1 satoshi] /buypheno/[phenoid] : returns the phenotype value for phenotype phenoid 83 | 84 | 85 | -------------------------------------------------------------------------------- /genome-server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os, sys, gzip 3 | import json 4 | import random 5 | 6 | from flask import Flask, render_template, jsonify, abort, make_response 7 | from flask import request, url_for 8 | from flask import send_from_directory 9 | 10 | #going to use Flask-SQLAlchemy to interact with the database 11 | from flask_sqlalchemy import SQLAlchemy 12 | 13 | # import from the 21 Developer Library 14 | from two1.lib.wallet import Wallet 15 | from two1.lib.bitserv.flask import Payment 16 | 17 | app = Flask(__name__) 18 | 19 | #going to serve all variants in a VCF file given at command line 20 | infile = gzip.open(sys.argv[1]) 21 | 22 | 23 | #DB -- set to test.db 24 | 25 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' 26 | db = SQLAlchemy(app) 27 | 28 | #Models 29 | class Variant(db.Model): 30 | __tablename__ = "variants" 31 | id = db.Column(db.Integer, primary_key=True) 32 | chr = db.Column(db.String(100)) 33 | pos = db.Column(db.Integer) 34 | rsid = db.Column(db.String(100)) 35 | ref = db.Column(db.String(10)) 36 | alt = db.Column(db.String(10)) 37 | geno = db.Column(db.String(3)) 38 | genolk = db.Column(db.String(20)) 39 | def __init__(self, chr, pos, rsid, ref, alt, geno, genolk): 40 | self.chr = chr 41 | self.pos = pos 42 | self.rsid = rsid 43 | self.ref = ref 44 | self.alt = alt 45 | self.geno = geno 46 | self.genolk = genolk 47 | @property 48 | def serialize(self): 49 | return{ 50 | 'id': self.id, 51 | 'chr': self.chr, 52 | 'pos': self.pos, 53 | 'rsid': self.rsid, 54 | 'ref': self.ref, 55 | 'alt': self.alt, 56 | 'geno': self.geno, 57 | 'genolk': self.genolk 58 | } 59 | @property 60 | def serialize_nogt(self): 61 | return{ 62 | 'id': self.id, 63 | 'chr': self.chr, 64 | 'pos': self.pos, 65 | 'rsid': self.rsid, 66 | 'ref': self.ref, 67 | 'alt': self.alt 68 | } 69 | 70 | 71 | class Phenotype(db.Model): 72 | __tablename__ = "phenos" 73 | id = db.Column(db.Integer, primary_key=True) 74 | pheno_name = db.Column(db.String(250)) 75 | pheno_value = db.Column(db.String(250)) 76 | def __init__(self, name, value): 77 | self.pheno_name = name 78 | self.pheno_value = value 79 | @property 80 | def serialize(self): 81 | return{ 82 | 'id': self.id, 83 | 'pheno_name': self.pheno_name, 84 | 'pheno_value': self.pheno_value 85 | } 86 | @property 87 | def serialize_nov(self): 88 | return{ 89 | 'id': self.id, 90 | 'pheno_name': self.pheno_name 91 | } 92 | 93 | 94 | db.create_all() 95 | 96 | # add some phenotypes 97 | new_pheno = Phenotype(name="height (cm)", value="175") 98 | db.session.add(new_pheno) 99 | db.session.commit() 100 | 101 | new_pheno = Phenotype(name="weight (lb)", value="145") 102 | db.session.add(new_pheno) 103 | db.session.commit() 104 | 105 | # add some genotypes 106 | 107 | line = infile.readline() 108 | while line: 109 | line = bytes.decode(line) 110 | line = line.strip().split() 111 | if line[0][0] == "#": 112 | line = infile.readline() 113 | continue 114 | chr = line[0] 115 | pos = line[1] 116 | snpid = line[2] 117 | ref = line[3] 118 | alt = line[4] 119 | fields = line[8].split(":") 120 | gtfields = line[9].split(":") 121 | gt = "9/9" 122 | gtlk = "NA,NA,NA" 123 | for i in range(len(fields)): 124 | f = fields[i] 125 | if f == "GT": 126 | gt = gtfields[i] 127 | elif f == "GL": 128 | gtlk = gtfields[i] 129 | print("LOADING:", chr, pos, gt) 130 | newvariant = Variant(chr=chr, pos=pos, rsid=snpid, ref=ref, alt=alt, geno=gt, genolk=gtlk) 131 | db.session.add(newvariant) 132 | line = infile.readline() 133 | 134 | db.session.commit() 135 | 136 | #Wallet 137 | wallet = Wallet() 138 | payment = Payment(app, wallet) 139 | 140 | # path to the bulk VCF files to sell 141 | vcf_path = os.path.dirname(os.path.realpath(__file__)) + '/vcffile' 142 | 143 | 144 | # simple content model: dictionary of files w/ prices 145 | files = {} 146 | 147 | # get a list of the files in the directory 148 | file_list = os.listdir(vcf_path) 149 | 150 | for file_id in range(len(file_list)): 151 | files[file_id] = file_list[file_id], 1000 152 | 153 | # endpoint to look up VCF files to buy 154 | @app.route('/vcf') 155 | def file_lookup(): 156 | return json.dumps(files) 157 | 158 | # return the price of the selected file 159 | def get_price_from_request(request): 160 | id = int(request.args.get('selection')) 161 | return files[id][1] 162 | 163 | # machine-payable endpoint that returns selected file if payment made 164 | @app.route('/buyvcf') 165 | @payment.required(get_price_from_request) 166 | def buy_file(): 167 | 168 | # extract selection from client request 169 | sel = int(request.args.get('selection')) 170 | 171 | # check if selection is valid 172 | # note you still pay for this 173 | if(sel < 0 or sel >= len(file_list)): 174 | return 'Invalid selection.' 175 | else: 176 | return send_from_directory(vcf_path,file_list[int(sel)]) 177 | 178 | def make_public_variant(variant): 179 | new_variant = {} 180 | for field in variant: 181 | if field == 'id': 182 | new_variant['uri'] = url_for('get_snp', chromosome=variant['chr'], position = variant['pos'], _external=True) 183 | else: 184 | new_variant[field] = variant[field] 185 | return new_variant 186 | 187 | def make_public_phenotype(pheno): 188 | new_pheno = {} 189 | for field in pheno: 190 | if field == 'id': 191 | new_pheno['uri'] = url_for('get_pheno', phenoid=pheno['id'], _external=True) 192 | else: 193 | new_pheno[field] = pheno[field] 194 | return new_pheno 195 | 196 | @app.route('/variants', methods=['GET']) 197 | def get_variants(): 198 | snpquery = db.session.query(Variant) 199 | return jsonify(variant_list = [make_public_variant(i.serialize_nogt) for i in snpquery.all()]) 200 | 201 | @app.route('/phenotypes', methods=['GET']) 202 | def get_phenos(): 203 | snpquery = db.session.query(Phenotype) 204 | return jsonify(pheno_list = [make_public_phenotype(i.serialize_nov) for i in snpquery.all()]) 205 | 206 | @app.route('/buyvariant//', methods=['GET']) 207 | @payment.required(1) 208 | def get_snp(chromosome, position): 209 | snpquery = db.session.query(Variant).filter(Variant.chr == chromosome).filter(Variant.pos == position) 210 | return jsonify(variant_list=[i.serialize for i in snpquery.all()]) 211 | 212 | @app.route('/buyphenotype/', methods=['GET']) 213 | @payment.required(1) 214 | def get_pheno(phenoid): 215 | phenoquery = db.session.query(Phenotype).filter(Phenotype.id == phenoid) 216 | return jsonify(pheno_list=[i.serialize for i in phenoquery.all()]) 217 | 218 | if __name__ == '__main__': 219 | app.run(host='0.0.0.0', debug=False) 220 | --------------------------------------------------------------------------------